text: /**
   * https://github.com/tsironis/lockr
   */
  ;(function(root, factory) {
      define('Lockr', factory(root, {}))
  }(this, function(root, Lockr) {
      'use strict'
  
      Lockr.prefix = ''
  
      Lockr._getPrefixedKey = function(key, options) {
          options = options || {}
  
          if (options.noPrefix) {
              return key
          } else {
              return this.prefix + key
          }
  
      }
  
      Lockr.set = function(key, value, options) {
          var query_key = this._getPrefixedKey(key, options)
  
          try {
              localStorage.setItem(query_key, JSON.stringify({
                  data: value
              }))
          } catch (e) {}
      }
  
      Lockr.get = function(key, missing, options) {
          var query_key = this._getPrefixedKey(key, options),
              value
  
          try {
              value = JSON.parse(localStorage.getItem(query_key))
          } catch (e) {
              if (localStorage[query_key]) {
                  value = {
                      data: localStorage.getItem(query_key)
                  }
              } else {
                  value = null
              }
          }
          
          if (value === null) {
              return missing
          } else if (typeof value === 'object' && typeof value.data !== 'undefined') {
              return value.data
          } else {
              return missing
          }
      }
  
      return Lockr
  }))
  
  /*!
   * TWOverflow v1.0.7
   *
   * Copyright (C) 2019 Relaxeaza
   * This work is free. You can redistribute it and/or modify it under the
   * terms of the Do What The Fuck You Want To Public License, Version 2,
   * as published by Sam Hocevar. See the LICENCE file for more details.
   */
  
  ;(function (window, undefined) {
  
  var $rootScope = injector.get('$rootScope')
  var transferredSharedDataService = injector.get('transferredSharedDataService')
  var modelDataService = injector.get('modelDataService')
  var socketService = injector.get('socketService')
  var routeProvider = injector.get('routeProvider')
  var eventTypeProvider = injector.get('eventTypeProvider')
  var windowDisplayService = injector.get('windowDisplayService')
  var windowManagerService = injector.get('windowManagerService')
  var angularHotkeys = injector.get('hotkeys')
  var armyService = injector.get('armyService')
  var villageService = injector.get('villageService')
  var mapService = injector.get('mapService')
  var $filter = injector.get('$filter')
  var $timeout = injector.get('$timeout')
  var storageService = injector.get('storageService')
  
  define('two/EventScope', [
      'queues/EventQueue'
  ], function (eventQueue) {
      var EventScope = function (windowId, onDestroy) {
          if (typeof windowId === 'undefined') {
              throw new Error('EventScope: no windowId')
          }
  
          this.windowId = windowId
          this.onDestroy = onDestroy || function () {}
          this.listeners = []
  
          var unregister = $rootScope.$on(eventTypeProvider.WINDOW_CLOSED, (event, templateName) => {
              if (templateName === '!' + this.windowId) {
                  this.destroy()
                  unregister()
              }
          })
      }
  
      EventScope.prototype.register = function (id, handler, _root) {
          if (_root) {
              this.listeners.push($rootScope.$on(id, handler))
          } else {
              eventQueue.register(id, handler)
              
              this.listeners.push(function () {
                  eventQueue.unregister(id, handler)
              })
          }
      }
  
      EventScope.prototype.destroy = function () {
          this.listeners.forEach((unregister) => {
              unregister()
              this.onDestroy()
          })
      }
  
      return EventScope
  })
  define('two/utils', [
      'helper/time',
      'helper/math'
  ], function (
      $timeHelper,
      $math
  ) {
      var notifTimeout = null
      var utils = {}
  
      /**
       * Gera um número aleatório aproximado da base.
       *
       * @param {Number} base - Número base para o calculo.
       */
      utils.randomSeconds = function (base) {
          base = parseInt(base, 10)
  
          var max = base + (base / 2)
          var min = base - (base / 2)
  
          return Math.round(Math.random() * (max - min) + min)
      }
  
      /**
       * Converte uma string com um tempo em segundos.
       *
       * @param {String} time - Tempo que será convertido (hh:mm:ss)
       */
      utils.time2seconds = function (time) {
          time = time.split(':')
          time[0] = parseInt(time[0], 10) * 60 * 60
          time[1] = parseInt(time[1], 10) * 60
          time[2] = parseInt(time[2], 10)
  
          return time.reduce(function (a, b) {
              return a + b
          })
      }
  
      /**
       * Emite notificação nativa do jogo.
       *
       * @param {String} type - success || error
       * @param {String} message - Texto a ser exibido
       */
      utils.emitNotif = function (type, message) {
          var eventType = type === 'success'
              ? eventTypeProvider.MESSAGE_SUCCESS
              : eventTypeProvider.MESSAGE_ERROR
  
          $rootScope.$broadcast(eventType, {
              message: message
          })
      }
  
  
      /**
       * Gera uma string com nome e coordenadas da aldeia
       *
       * @param {Object} village - Dados da aldeia
       * @return {String}
       */
      utils.genVillageLabel = function (village) {
          return village.name + ' (' + village.x + '|' + village.y + ')'
      }
  
      /**
       * Verifica se uma coordenada é válida.
       * 00|00
       * 000|00
       * 000|000
       * 00|000
       *
       * @param {String} xy - Coordenadas
       * @return {Boolean}
       */
      utils.isValidCoords = function (xy) {
          return /\s*\d{2,3}\|\d{2,3}\s*/.test(xy)
      }
  
      /**
       * Validação de horario e data de envio. Exmplo: 23:59:00:999 30/12/2016
       *
       * @param  {String}  dateTime
       * @return {Boolean}
       */
      utils.isValidDateTime = function (dateTime) {
          return /^\s*([01][0-9]|2[0-3]):[0-5]\d:[0-5]\d(:\d{1,3})? (0[1-9]|[12][0-9]|3[0-1])\/(0[1-9]|1[0-2])\/\d{4}\s*$/.test(dateTime)
      }
  
      /**
       * Inverte a posição do dia com o mês.
       */
      utils.fixDate = function (dateTime) {
          var dateAndTime = dateTime.split(' ')
          var time = dateAndTime[0]
          var date = dateAndTime[1].split('/')
  
          return time + ' ' + date[1] + '/' + date[0] + '/' + date[2]
      }
  
      /**
       * Gera um id unico
       *
       * @return {String}
       */
      utils.guid = function () {
          return Math.floor((Math.random()) * 0x1000000).toString(16)
      }
  
      /**
       * Verifica se um elemento é pertencente a outro elemento.
       *
       * @param  {Element} elem - Elemento referencia
       * @param  {String} selector - Selector CSS do elemento no qual será
       *   será verificado se tem relação com o elemento indicado.
       * @return {Boolean}
       */
      utils.matchesElem = function (elem, selector) {
          if ($(elem).parents(selector).length) {
              return true
          }
  
          return false
      }
  
      /**
       * Obtem o timestamp de uma data em string.
       * Formato da data: mês/dia/ano
       * Exmplo de entrada: 23:59:59:999 12/30/2017
       *
       * @param  {String} dateString - Data em formato de string.
       * @return {Number} Timestamp (milisegundos)
       */
      utils.getTimeFromString = function (dateString, offset) {
          var dateSplit = dateString.trim().split(' ')
          var time = dateSplit[0].split(':')
          var date = dateSplit[1].split('/')
  
          var hour = time[0]
          var min = time[1]
          var sec = time[2]
          var ms = time[3] || null
  
          var month = parseInt(date[0], 10) - 1
          var day = date[1]
          var year = date[2]
  
          var date = new Date(year, month, day, hour, min, sec, ms)
  
          return date.getTime() + (offset || 0)
      }
  
      /**
       * Formata milisegundos em hora/data
       *
       * @return {String} Data e hora formatada
       */
      utils.formatDate = function (ms, format) {
          return $filter('readableDateFilter')(
              ms,
              null,
              $rootScope.GAME_TIMEZONE,
              $rootScope.GAME_TIME_OFFSET,
              format || 'HH:mm:ss dd/MM/yyyy'
          )
      }
  
      /**
       * Obtem a diferença entre o timezone local e do servidor.
       *
       * @type {Number}
       */
      utils.getTimeOffset = function () {
          var localDate = $timeHelper.gameDate()
          var localOffset = localDate.getTimezoneOffset() * 1000 * 60
          var serverOffset = $rootScope.GAME_TIME_OFFSET
  
          return localOffset + serverOffset
      }
  
      utils.xhrGet = function (url, _callback, _dataType) {
          if (!url) {
              return false
          }
  
          _dataType = _dataType || 'text'
          _callback = _callback || function () {}
  
          var xhr
  
          xhr = new XMLHttpRequest()
          xhr.open('GET', url, true)
          xhr.responseType = _dataType
          xhr.addEventListener('load', function () {
              _callback(xhr.response)
          }, false)
  
          xhr.send()
      }
  
      utils.obj2selectOptions = function (obj, _includeIcon) {
          var list = []
          var item
          var i
  
          for (i in obj) {
              item = {
                  name: obj[i].name,
                  value: obj[i].id
              }
  
              if (_includeIcon) {
                  item.leftIcon = obj[i].icon
              }
  
              list.push(item)
          }
  
          return list
      }
  
      /**
       * @param {Object} origin - Objeto da aldeia origem.
       * @param {Object} target - Objeto da aldeia alvo.
       * @param {Object} units - Exercito usado no ataque como referência
       * para calcular o tempo.
       * @param {String} type - Tipo de comando (attack,support,relocate)
       * @param {Object} officers - Oficiais usados no comando (usados para efeitos)
       *
       * @return {Number} Tempo de viagem
       */
      utils.getTravelTime = function (origin, target, units, type, officers) {
          var useEffects = false
          var targetIsBarbarian = target.character_id === null
          var targetIsSameTribe = target.character_id && target.tribe_id &&
                  target.tribe_id === modelDataService.getSelectedCharacter().getTribeId()
  
          if (type === 'attack') {
              if ('supporter' in officers) {
                  delete officers.supporter
              }
  
              if (targetIsBarbarian) {
                  useEffects = true
              }
          } else if (type === 'support') {
              if (targetIsSameTribe) {
                  useEffects = true
              }
  
              if ('supporter' in officers) {
                  useEffects = true
              }
          }
  
          var army = {
              units: units,
              officers: angular.copy(officers)
          }
  
          var travelTime = armyService.calculateTravelTime(army, {
              barbarian: targetIsBarbarian,
              ownTribe: targetIsSameTribe,
              officers: officers,
              effects: useEffects
          }, type)
  
          var distance = $math.actualDistance(origin, target)
  
          var totalTravelTime = armyService.getTravelTimeForDistance(
              army,
              travelTime,
              distance,
              type
          )
  
          return totalTravelTime * 1000
      }
  
      return utils
  })
  
  define('two/ready', [
      'conf/gameStates'
  ], function (
      GAME_STATES
  ) {
      var ready = function (callback, which) {
          which = which || ['map']
  
          var readyStep = function (item) {
              which = which.filter(function (_item) {
                  return _item !== item
              })
  
              if (!which.length) {
                  callback()
              }
          }
  
          var handlers = {
              'map': function () {
                  var mapScope = transferredSharedDataService.getSharedData('MapController')
  
                  if (mapScope.isInitialized) {
                      return readyStep('map')
                  }
  
                  $rootScope.$on(eventTypeProvider.MAP_INITIALIZED, function () {
                      readyStep('map')
                  })
              },
              'tribe_relations': function () {
                  var $player = modelDataService.getSelectedCharacter()
  
                  if ($player) {
                      var $tribeRelations = $player.getTribeRelations()
  
                      if (!$player.getTribeId() || $tribeRelations) {
                          return readyStep('tribe_relations')
                      }
                  }
  
                  var unbind = $rootScope.$on(eventTypeProvider.TRIBE_RELATION_LIST, function () {
                      unbind()
                      readyStep('tribe_relations')
                  })
              },
              'initial_village': function () {
                  var $gameState = modelDataService.getGameState()
  
                  if ($gameState.getGameState(GAME_STATES.INITIAL_VILLAGE_READY)) {
                      return readyStep('initial_village')
                  }
  
                  $rootScope.$on(eventTypeProvider.GAME_STATE_INITIAL_VILLAGE_READY, function () {
                      readyStep('initial_village')
                  })
              },
              'all_villages_ready': function () {
                  var $gameState = modelDataService.getGameState()
  
                  if ($gameState.getGameState(GAME_STATES.ALL_VILLAGES_READY)) {
                      return readyStep('all_villages_ready')
                  }
  
                  $rootScope.$on(eventTypeProvider.GAME_STATE_ALL_VILLAGES_READY, function () {
                      readyStep('all_villages_ready')
                  })
              }
          }
  
          var mapScope = transferredSharedDataService.getSharedData('MapController')
  
          if (!mapScope) {
              return setTimeout(function () {
                  ready(callback, which)
              }, 100)
          }
  
          which.forEach(function (readyItem) {
              handlers[readyItem]()
          })
      }
  
      return ready
  })
  
  require([
      'two/ready',
      'Lockr'
  ], function (
      ready,
      Lockr
  ) {
      ready(function () {
          var $player = modelDataService.getSelectedCharacter()
  
          // Lockr settings
          Lockr.prefix = $player.getId() + '_twOverflow_' + $player.getWorldId() + '-'
      })
  })
  
  define('two/language', ['helper/i18n'], function (i18n) {
      var initialized = false
      var moduleLanguages = {}
      var selectedLanguage
      var defaultLanguage = 'en_us'
      var sharedLanguages = {
          'en_dk': 'en_us',
          'pt_pt': 'pt_br'
      }
  
      var injectLanguage = function (moduleId) {
          if (!initialized) {
              throw new Error('Language module not initialized.')
              return false
          }
  
          if (!(moduleId in moduleLanguages)) {
              throw new Error('Language data for ' + moduleId + ' does not exists.')
              return false
          }
  
          var moduleLanguage = moduleLanguages[moduleId][selectedLanguage] || moduleLanguages[moduleId][defaultLanguage]
  
          i18n.setJSON(moduleLanguage)
      }
  
      var selectLanguage = function (language) {
          selectedLanguage = language
          
          if (selectedLanguage in sharedLanguages) {
              selectedLanguage = sharedLanguages[selectedLanguage]
          }
  
          return selectedLanguage
      }
  
      var twoLanguage = {}
  
      twoLanguage.add = function (moduleId, languages) {
          if (!initialized) {
              twoLanguage.init()
          }
  
          if (moduleId in moduleLanguages) {
              console.log('Language data for ' + moduleId + ' already exists. Ignoring...')
              return false
          }
  
          moduleLanguages[moduleId] = languages
  
          injectLanguage(moduleId)
      }
  
      twoLanguage.init = function () {
          if (initialized) {
              return false
          }
  
          initialized = true
          selectedLanguage = selectLanguage($rootScope.loc.ale)
  
          // trigger eventTypeProvider.LANGUAGE_SELECTED_CHANGED you dumb fucks
          $rootScope.$watch('loc.ale', function (newValue, oldValue) {
              if (newValue === oldValue) {
                  return false
              }
  
              selectLanguage($rootScope.loc.ale)
  
              var moduleId
  
              for (moduleId in moduleLanguages) {
                  injectLanguage(moduleId)
              }
          })
      }
  
      return twoLanguage
  })
  
  define('two/Settings', [
      'Lockr'
  ], function (
      Lockr
  ) {
      var textObjectCommon = 'common'
      var noop = function () {}
      var hasOwn = Object.prototype.hasOwnProperty
  
      var generateDiff = function (before, after) {
          var changes = {}
          var id
  
          for (id in before) {
              if (hasOwn.call(after, id)) {
                  if (!angular.equals(before[id], after[id])) {
                      changes[id] = after[id]
                  }
              } else {
                  changes[id] = before[id]
              }
          }
  
          return changes
      }
  
      var generateDefaults = function (map) {
          var key
          var defaults = {}
  
          for (key in map) {
              defaults[key] = map[key].default
          }
  
          return defaults
      }
  
      var disabledOption = function () {
          return {
              name: $filter('i18n')('disabled', $rootScope.loc.ale, textObjectCommon),
              value: false
          }
      }
  
      var Settings = function (configs) {
          this.settingsMap = configs.settingsMap
          this.storageKey = configs.storageKey
          this.defaults = generateDefaults(this.settingsMap)
          this.settings = Lockr.get(this.storageKey, this.defaults, true)
          this.events = {
              settingsChange: noop
          }
          this.injected = false
      }
  
      Settings.prototype.getSetting = function (id) {
          return this.settings[id]
      }
  
      Settings.prototype.getSettings = function () {
          return this.settings
      }
  
      Settings.prototype.getDefault = function (id) {
          return hasOwn.call(this.defaults, id) ? this.defaults[id] : undefined
      }
  
      Settings.prototype.store = function () {
          Lockr.set(this.storageKey, this.settings)
      }
  
      Settings.prototype.setSetting = function (id, value, opt) {
          var changes
          var before
          var after
          var update = false
  
          if (hasOwn.call(this.settingsMap, id)) {
              before = angular.copy(this.settings)
              this.settings[id] = value
              after = angular.copy(this.settings)
              changes = generateDiff(before, after)
  
              if (this.settingsMap[id].update) {
                  update = true
              }
  
              this.store()
              this.updateInjectedScope()
              this.events.settingsChange(changes, update, opt || {})
  
              return true
          }
  
          return false
      }
  
      Settings.prototype.setSettings = function (values, opt) {
          var changes
          var before = angular.copy(this.settings)
          var after
          var update = false
          var id
  
          for (id in values) {
              if (hasOwn.call(this.settingsMap, id)) {
                  this.settings[id] = values[id]
  
                  if (!update && this.settingsMap[id].update) {
                      update = true
                  }
              }
          }
  
          after = angular.copy(this.settings)
          changes = generateDiff(before, after)
  
          this.store()
          this.updateInjectedScope()
          this.events.settingsChange(changes, update, opt || {})
  
          return true
      }
  
      Settings.prototype.resetSetting = function (id, opt) {
          this.setSetting(id, this.defaults[id], opt)
  
          return true
      }
  
      Settings.prototype.resetSettings = function (opt) {
          this.setSettings(angular.copy(this.defaults), opt)
  
          return true
      }
  
      Settings.prototype.eachSetting = function (callback) {
          var id
          var value
          var map
  
          for (id in this.settings) {
              map = this.settingsMap[id]
  
              if (map.inputType === 'checkbox') {
                  callback.call(this, id, !!this.settings[id], map)
              } else {
                  callback.call(this, id, this.settings[id], map)
              }
          }
      }
  
      Settings.prototype.onSettingsChange = function (callback) {
          if (typeof callback === 'function') {
              this.events.settingsChange = callback
          }
      }
  
      Settings.prototype.injectSettings = function ($scope, opt) {
          var id
          var map
  
          this.injected = {
              $scope: $scope,
              opt: opt
          }
  
          $scope.settings = this.encodeSettings(opt)
  
          angular.forEach(this.settingsMap, function (map, id) {
              if (map.inputType === 'select') {
                  $scope.$watch(function () {
                      return $scope.settings[id]
                  }, function (value) {
                      if (map.multiSelect) {
                          if (!value.length) {
                              $scope.settings[id] = [disabledOption()]
                          }
                      } else if (!value) {
                          $scope.settings[id] = disabledOption()
                      }
                  }, true)
              }
          })
      }
  
      Settings.prototype.updateInjectedScope = function () {
          if (!this.injected) {
              return false
          }
          
          this.injected.$scope.settings = this.encodeSettings(this.injected.opt)
      }
  
      Settings.prototype.encodeSettings = function (opt) {
          var encoded = {}
          var presets = modelDataService.getPresetList().getPresets()
          var groups = modelDataService.getGroupList().getGroups()
          var multiValues
  
          opt = opt || {}
  
          this.eachSetting(function (id, value, map) {
              if (map.inputType === 'select') {
                  if (!value && map.disabledOption) {
                      encoded[id] = map.multiSelect ? [disabledOption()] : disabledOption()
                      return
                  }
  
                  switch (map.type) {
                  case 'presets':
                      if (map.multiSelect) {
                          multiValues = []
  
                          value.forEach(function (presetId) {
                              if (!presets[presetId]) {
                                  return
                              }
  
                              multiValues.push({
                                  name: presets[presetId].name,
                                  value: presetId
                              })
                          })
  
                          encoded[id] = multiValues.length ? multiValues : [disabledOption()]
                      } else {
                          if (!presets[value] && map.disabledOption) {
                              encoded[id] = disabledOption()
                              return
                          }
  
                          encoded[id] = {
                              name: presets[value].name,
                              value: value
                          }
                      }
  
                      break
                  case 'groups':
                      if (map.multiSelect) {
                          multiValues = []
  
                          value.forEach(function (groupId) {
                              if (!groups[groupId]) {
                                  return
                              }
  
                              multiValues.push({
                                  name: groups[groupId].name,
                                  value: groupId,
                                  leftIcon: groups[groupId].icon
                              })
                          })
  
                          encoded[id] = multiValues.length ? multiValues : [disabledOption()]
                      } else {
                          if (!groups[value] && map.disabledOption) {
                              encoded[id] = disabledOption()
                              return
                          }
  
                          encoded[id] = {
                              name: groups[value].name,
                              value: value
                          }
                      }
  
                      break
                  default:
                      encoded[id] = {
                          name: opt.textObject ? $filter('i18n')(value, $rootScope.loc.ale, opt.textObject) : value,
                          value: value
                      }
  
                      if (opt.multiSelect) {
                          encoded[id] = [encoded[id]]
                      }
  
                      break
                  }
              } else {
                  encoded[id] = value
              }
          })
  
          return encoded
      }
  
      Settings.prototype.decodeSettings = function (encoded) {
          var id
          var decoded = {}
          var multiValues
          var map
  
          for (id in encoded) {
              map = this.settingsMap[id]
  
              if (map.inputType === 'select') {
                  if (map.multiSelect) {
                      if (encoded[id].length === 1 && encoded[id][0].value === false) {
                          decoded[id] = []
                      } else {
                          multiValues = []
  
                          encoded[id].forEach(function (item) {
                              multiValues.push(item.value)
                          })
  
                          decoded[id] = multiValues
                      }
                  } else {
                      decoded[id] = encoded[id].value
                  }
              } else {
                  decoded[id] = encoded[id]
              }
          }
  
          return decoded
      }
  
      Settings.encodeList = function (list, opt) {
          var encoded = []
          var prop
          var value
  
          opt = opt || {}
  
          if (opt.disabled) {
              encoded.push(disabledOption())
          }
  
          switch (opt.type) {
          case 'keys':
              for (prop in list) {
                  value = list[prop]
  
                  encoded.push({
                      name: prop,
                      value: prop
                  })
              }
  
              break
          case 'groups':
              for (prop in list) {
                  value = list[prop]
  
                  encoded.push({
                      name: value.name,
                      value: value.id,
                      leftIcon: value.icon
                  })
              }
  
              break
          case 'presets':
              for (prop in list) {
                  value = list[prop]
  
                  encoded.push({
                      name: value.name,
                      value: value.id
                  })
              }
  
              break
          case 'values':
          default:
              for (prop in list) {
                  value = list[prop]
  
                  encoded.push({
                      name: opt.textObject ? $filter('i18n')(value, $rootScope.loc.ale, opt.textObject) : value,
                      value: value
                  })
              }
          }
  
          return encoded
      }
  
      Settings.disabledOption = disabledOption
  
      return Settings
  })
  
  require([
      'two/language',
      'two/ready'
  ], function (
      twoLanguage,
      ready
  ) {
      ready(function () {
          twoLanguage.add('core', {"cs_cz":{"common":{"start":"Spustit","started":"Started","pause":"Pozastavit","paused":"Pozastaveno","stop":"Stop","stopped":"Stopped","status":"Stav","none":"Žádný","info":"Informace","settings":"Nastavení","others":"Ostatní","village":"Village","villages":"Vesnice","building":"Building","buildings":"Buildings","level":"Level","registers":"Registry","filters":"Filtry","add":"Přidat","waiting":"Vyčkává","attack":"Útok","support":"Podpora","relocate":"Přesun","activate":"Aktivovat","deactivate":"Zrušit","units":"Jednotky","officers":"Důstojníci","origin":"Původ","target":"Cíl","save":"Uložit","logs":"Logs","headquarter":"Velitelství","barracks":"Kasárny","tavern":"Hospoda","hospital":"Nemocnice","preceptory":"Komturie","chapel":"Kaple","church":"Kostel","academy":"Akademie","rally_point":"Shromaždiště","statue":"Socha","market":"Tržnice","timber_camp":"Dřevorubec","clay_pit":"Jíloviště","iron_mine":"Důl na železo","farm":"Farma","warehouse":"Sklad","wall":"Zeď","spear":"Kopiník","sword":"Šermíř","axe":"Sekerník","archer":"Lukostřelec","light_cavalry":"Lehká jízda","mounted_archer":"Jízdní lučištník","heavy_cavalry":"Těžká jízka","ram":"Beranidlo","catapult":"Katapult","doppelsoldner":"Bersekr","trebuchet":"Trebuchet","snob":"Nobleman","knight":"Paladin","no-results":"No results...","selected":"Selected","now":"Now","costs":"Costs","duration":"Duration","points":"Points","player":"Player","players":"Players","next_features":"Next features","misc":"Miscellaneous","colors":"Colors","reset":"Reset","here":"here","disabled":"— Disabled —","cancel":"Cancel","actions":"Actions","remove":"Remove","started_at":"Started at","arrive":"Arrive","settings_saved":"Settings saved"}},"de_de":{"common":{"start":"Start","started":"Gestartet","pause":"Pause","paused":"Pausiert","stop":"Anhalten","stopped":"Angehalten","status":"Status","none":"Keine","info":"Informationen","settings":"Einstellungen","others":"Andere","village":"Dorf","villages":"Dorf","building":"Gebäude","buildings":"Gebäude","level":"Stufe","registers":"Protokolle","filters":"Filter","add":"Hinzufügen","waiting":"In Wartestellung","attack":"Angriff","support":"Unterstützen","relocate":"Umsiedeln","activate":"Aktivieren","deactivate":"Deaktivieren","units":"Einheiten","officers":"Offiziere","origin":"Herkunft","target":"Ziel","save":"Speichern","logs":"Log-Dateien","headquarter":"Hauptgebäude","barracks":"Kaserne","tavern":"Taverne","hospital":"Lazarett","preceptory":"Ordenshalle","chapel":"Kapelle","church":"Kirche","academy":"Akademie","rally_point":"Sammelplatz","statue":"Statue","market":"Markt","timber_camp":"Holzfäller","clay_pit":"Lehmgrube","iron_mine":"Eisenmine","farm":"Bauernhof","warehouse":"Speicher","wall":"Wall","spear":"Speerkämpfer","sword":"Schwertkämpfer","axe":"Axtkämpfer","archer":"Bogenschützen","light_cavalry":"Leichte Kavallerie","mounted_archer":"Berittener Bogenschütze","heavy_cavalry":"Schwere Kavallerie","ram":"Rammbock","catapult":"Katapult","doppelsoldner":"Berserker","trebuchet":"Tribok","snob":"Adelsgeschlecht","knight":"Paladin","no-results":"Keine Ergebnisse...","selected":"Ausgewählt","now":"Jetzt","costs":"Kosten","duration":"Dauer","points":"Punkte","player":"Spieler","players":"Spieler","next_features":"Kommende Features","misc":"Verschiedenes","colors":"Farben","reset":"Reset","here":"here","disabled":"— Deaktiviert —","cancel":"Cancel","actions":"Actions","remove":"Remove","started_at":"Started at","arrive":"Arrive","settings_saved":"Einstellungen gespeichert"}},"en_us":{"common":{"start":"Start","started":"Started","pause":"Pause","paused":"Paused","stop":"Stop","stopped":"Stopped","status":"Status","none":"None","info":"Information","settings":"Settings","others":"Others","village":"Village","villages":"Villages","building":"Building","buildings":"Buildings","level":"Level","registers":"Logs","filters":"Filters","add":"Add","waiting":"Waiting","attack":"Attack","support":"Support","relocate":"Transfer","activate":"Activate","deactivate":"Disable","units":"Units","officers":"Officers","origin":"Origin","target":"Target","save":"Save","logs":"Logs","headquarter":"Headquarters","barracks":"Barracks","tavern":"Tavern","hospital":"Hospital","preceptory":"Hall of Orders","chapel":"Chapel","church":"Church","academy":"Academy","rally_point":"Rally Point","statue":"Statue","market":"Market","timber_camp":"Timber Camp","clay_pit":"Clay Pit","iron_mine":"Iron Mine","farm":"Farm","warehouse":"Warehouse","wall":"Wall","spear":"Spearman","sword":"Swordsman","axe":"Axe Fighter","archer":"Archer","light_cavalry":"Light Cavalry","mounted_archer":"Mounted Archer","heavy_cavalry":"Heavy Cavalry","ram":"Ram","catapult":"Catapult","doppelsoldner":"Berserker","trebuchet":"Trebuchet","snob":"Nobleman","knight":"Paladin","no-results":"No results...","selected":"Selected","now":"Now","costs":"Costs","duration":"Duration","points":"Points","player":"Player","players":"Players","next_features":"Next features","misc":"Miscellaneous","colors":"Colors","reset":"Reset","here":"here","disabled":"— Disabled —","cancel":"Cancel","actions":"Actions","remove":"Remove","started_at":"Started at","arrive":"Arrive","settings_saved":"Settings saved"}},"es_es":{"common":{"start":"Comenzar","started":"Started","pause":"Pausa","paused":"Pausado","stop":"Stop","stopped":"Stopped","status":"Estatus","none":"Ninguno","info":"Información","settings":"Controles","others":"Otros","village":"Village","villages":"Villas","building":"Building","buildings":"Buildings","level":"Level","registers":"Registra","filters":"Filtros","add":"Agregar","waiting":"Esperando","attack":"Ataque","support":"Soporte","relocate":"Transferir","activate":"Activar","deactivate":"Inhabilitado","units":"Unidades","officers":"Oficiales","origin":"Origen","target":"Objetivo","save":"Guardar","logs":"Logs","headquarter":"Sede general","barracks":"Cuarteles","tavern":"Taberna","hospital":"Hospital","preceptory":"Sala de Ordenes","chapel":"Capilla","church":"Iglesia","academy":"Academia","rally_point":"Punto de reunión","statue":"Estatua","market":"Mercado","timber_camp":"Campamento de madera","clay_pit":"Pozo de Arcilla","iron_mine":"Mina de Hierro","farm":"Granja","warehouse":"Depósito","wall":"Pared","spear":"Lancero","sword":"Espadachín","axe":"Luchador de Hacha","archer":"Arquero","light_cavalry":"Caballería ligera","mounted_archer":"Arquería montada","heavy_cavalry":"Caballería Pesada","ram":"Martillo","catapult":"Catapulta","doppelsoldner":"Frenético","trebuchet":"Trebuchet","snob":"Nobleman","knight":"Paladin","no-results":"No results...","selected":"Selected","now":"Now","costs":"Costs","duration":"Duration","points":"Points","player":"Player","players":"Players","next_features":"Next features","misc":"Miscellaneous","colors":"Colors","reset":"Reset","here":"here","disabled":"— Disabled —","cancel":"Cancel","actions":"Actions","remove":"Remove","started_at":"Started at","arrive":"Arrive","settings_saved":"Settings saved"}},"pl_pl":{"common":{"start":"Start","started":"Uruchomiony","pause":"Pauza","paused":"Wstrzymany","stop":"Zatrzymany","stopped":"Zatrzymany","status":"Status","none":"Żaden","info":"Informacje","settings":"Ustawienia","others":"Inne","village":"Wioska","villages":"Wioski","building":"Budynek","buildings":"Budynki","level":"Poziom","registers":"Rejestry","filters":"Filtry","add":"Dodaj","waiting":"Oczekujące","attack":"Atak","support":"Wsparcie","relocate":"Przeniesienie","activate":"Włącz","deactivate":"Wyłącz","units":"Jednostki","officers":"Oficerowie","origin":"Źródło","target":"Cel","save":"Zapisz","logs":"Logi","headquarter":"Ratusz","barracks":"Koszary","tavern":"Tawerna","hospital":"Szpital","preceptory":"Komturia","chapel":"Kaplica","church":"Kościół","academy":"Akademia","rally_point":"Plac","statue":"Piedestał","market":"Rynek","timber_camp":"Tartak","clay_pit":"Kopalnia gliny","iron_mine":"Huta żelaza","farm":"Farma","warehouse":"Magazyn","wall":"Mur","spear":"Pikinier","sword":"Miecznik","axe":"Topornik","archer":"Łucznik","light_cavalry":"Lekki kawalerzysta","mounted_archer":"Łucznik konny","heavy_cavalry":"Ciężki kawalerzysta","ram":"Taran","catapult":"Katapulta","doppelsoldner":"Berserker","trebuchet":"Trebusz","snob":"Szlachcic","knight":"Rycerz","no-results":"Brak wyników...","selected":"Wybrana","now":"Teraz","costs":"Koszty","duration":"Czas trwania","points":"Punkty","player":"Gracz","players":"Gracze","next_features":"Następne funkcje","misc":"Różne","colors":"Kolory","reset":"Reset","here":"here","disabled":"— Wyłączony —","cancel":"Cancel","actions":"Actions","remove":"Remove","started_at":"Started at","arrive":"Arrive","settings_saved":"Ustawienia zapisane"}},"pt_br":{"common":{"start":"Iniciar","started":"Iniciado","pause":"Pausar","paused":"Pausado","stop":"Parar","stopped":"Parado","status":"Status","none":"Nenhum","info":"Informações","settings":"Configurações","others":"Outros","village":"Aldeia","villages":"Aldeias","building":"Edifício","buildings":"Edifícios","level":"Nível","registers":"Registros","filters":"Filtros","add":"Adicionar","waiting":"Em espera","attack":"Ataque","support":"Apoio","relocate":"Transferência","activate":"Ativar","deactivate":"Desativar","units":"Unidades","officers":"Oficiais","origin":"Origem","target":"Alvo","save":"Salvar","logs":"Eventos","headquarter":"Edifício Principal","barracks":"Quartel","tavern":"Taverna","hospital":"Hospital","preceptory":"Salão das Ordens","chapel":"Capela","church":"Igreja","academy":"Academia","rally_point":"Ponto de Encontro","statue":"Estátua","market":"Mercado","timber_camp":"Bosque","clay_pit":"Poço de Argila","iron_mine":"Mina de Ferro","farm":"Fazenda","warehouse":"Armazém","wall":"Muralha","spear":"Lanceiro","sword":"Espadachim","axe":"Viking","archer":"Arqueiro","light_cavalry":"Cavalaria Leve","mounted_archer":"Arqueiro Montado","heavy_cavalry":"Cavalaria Pesada","ram":"Aríete","catapult":"Catapulta","doppelsoldner":"Berserker","trebuchet":"Trabuco","snob":"Nobre","knight":"Paladino","no-results":"Sem resultados...","selected":"Selecionado","now":"Agora","costs":"Custos","duration":"Duração","points":"Pontos","player":"Jogador","players":"Jogadores","next_features":"Próximas funcionalidades","misc":"Diversos","colors":"Cores","reset":"Resetar","here":"aqui","disabled":"— Desativado —","cancel":"Cancelar","actions":"Ações","remove":"Remover","started_at":"Iniciado em","arrive":"Chegada","settings_saved":"Configurações salvas"}}})
      })
  })
  
  define('two/attackView', [
      'two/ready',
      'two/utils',
      'two/attackView/columnTypes',
      'two/attackView/commandTypes',
      'two/attackView/filterTypes',
      'two/attackView/unitSpeedOrder',
      'conf/unitTypes',
      'Lockr',
      'helper/math',
      'helper/mapconvert',
      'struct/MapData',
      'queues/EventQueue',
      'models/CommandModel'
  ], function (
      ready,
      utils,
      COLUMN_TYPES,
      COMMAND_TYPES,
      FILTER_TYPES,
      UNIT_SPEED_ORDER,
      UNIT_TYPES,
      Lockr,
      math,
      convert,
      mapData,
      eventQueue,
      CommandModel
  ) {
      var initialized = false
      var listeners = {}
      var overviewService = injector.get('overviewService')
      var globalInfoModel
      var commands = []
      var commandQueue
      var commandQueueEnabled = false
      var filters = {}
      var filterParams = {}
      var sorting = {
          reverse: false,
          column: COLUMN_TYPES.TIME_COMPLETED
      }
      var COMMAND_ORDER = [
          'ATTACK',
          'SUPPORT',
          'RELOCATE'
      ]
      var STORAGE_KEYS = {
          FILTERS: 'attack_view_filters'
      }
      var INCOMING_UNITS_FILTER = {}
      var COMMAND_TYPES_FILTER = {}
  
      var resetFilters = function () {
          filters = {}
          filters[FILTER_TYPES.COMMAND_TYPES] = {}
          filters[FILTER_TYPES.VILLAGE] = angular.copy(COMMAND_TYPES_FILTER)
          filters[FILTER_TYPES.INCOMING_UNITS] = angular.copy(INCOMING_UNITS_FILTER)
      }
  
      var formatFilters = function () {
          var toArray = [FILTER_TYPES.COMMAND_TYPES]
          var currentVillageId = modelDataService.getSelectedVillage().getId()
          var arrays = {}
          var i
          var j
  
          // format filters for backend
          for (i = 0; i < toArray.length; i++) {
              for (j in filters[toArray[i]]) {
                  if (!arrays[toArray[i]]) {
                      arrays[toArray[i]] = []
                  }
  
                  if (filters[toArray[i]][j]) {
                      switch (toArray[i]) {
                      case FILTER_TYPES.COMMAND_TYPES:
                          if (j === COMMAND_TYPES.ATTACK) {
                              arrays[toArray[i]].push(COMMAND_TYPES.ATTACK)
                          } else if (j === COMMAND_TYPES.SUPPORT) {
                              arrays[toArray[i]].push(COMMAND_TYPES.SUPPORT)
                          } else if (j === COMMAND_TYPES.RELOCATE) {
                              arrays[toArray[i]].push(COMMAND_TYPES.RELOCATE)
                          }
                          break
                      }
                  }
              }
          }
  
          filterParams = arrays
          filterParams.village = filters[FILTER_TYPES.VILLAGE] ? [currentVillageId] : []
      }
  
      /**
       * Command was sent.
       */
      var onCommandIncomming = function () {
          // we can never know if the command is currently visible (because of filters, sorting and stuff) -> reload
          attackView.loadCommands()
      }
  
      /**
       * Command was cancelled.
       *
       * @param {Object} event unused
       * @param {Object} data The backend-data
       */
      var onCommandCancelled = function (event, data) {
          eventQueue.trigger(eventTypeProvider.ATTACK_VIEW_COMMAND_CANCELLED, [data.id || data.command_id])
      }
  
      /**
       * Command ignored.
       *
       * @param {Object} event unused
       * @param {Object} data The backend-data
       */
      var onCommandIgnored = function (event, data) {
          var i
  
          for (i = 0; i < commands.length; i++) {
              if (commands[i].command_id === data.command_id) {
                  commands.splice(i, 1)
              }
          }
  
          eventQueue.trigger(eventTypeProvider.ATTACK_VIEW_COMMAND_IGNORED, [data.command_id])
      }
  
      /**
       * Village name changed.
       *
       * @param {Object} event unused
       * @param {Object} data The backend-data
       */
      var onVillageNameChanged = function (event, data) {
          var i
  
          for (i = 0; i < commands.length; i++) {
              if (commands[i].target_village_id === data.village_id) {
                  commands[i].target_village_name = data.name
                  commands[i].targetVillage.name = data.name
              } else if (commands[i].origin_village_id === data.village_id) {
                  commands[i].origin_village_name = data.name
                  commands[i].originVillage.name = data.name
              }
          }
  
          eventQueue.trigger(eventTypeProvider.ATTACK_VIEW_VILLAGE_RENAMED, [data])
      }
  
      var onVillageSwitched = function (e, newVillageId) {
          if (filterParams[FILTER_TYPES.VILLAGE].length) {
              filterParams[FILTER_TYPES.VILLAGE] = [newVillageId]
  
              attackView.loadCommands()
          }
      }
  
      /**
       * @param {CommandModel} command
       * @return {String} Slowest unit
       */
      var getSlowestUnit = function (command) {
          var commandDuration = command.model.duration
          var units = {}
          var origin = { x: command.origin_x, y: command.origin_y }
          var target = { x: command.target_x, y: command.target_y }
          var travelTimes = []
  
          UNIT_SPEED_ORDER.forEach(function (unit) {
              units[unit] = 1
              
              travelTimes.push({
                  unit: unit,
                  duration: utils.getTravelTime(origin, target, units, command.command_type, {})
              })
          })
  
          travelTimes = travelTimes.map(function (travelTime) {
              travelTime.duration = Math.abs(travelTime.duration - commandDuration)
              return travelTime
          }).sort(function (a, b) {
              return a.duration - b.duration
          })
  
          return travelTimes[0].unit
      }
  
      /**
       * Sort a set of villages by distance from a specified village.
       *
       * @param {Array[{x: Number, y: Number}]} villages List of village that will be sorted.
       * @param {VillageModel} origin
       * @return {Array} Sorted villages
       */
      var sortByDistance = function (villages, origin) {
          var distA
          var distB
  
          return villages.sort(function (villageA, villageB) {
              distA = math.actualDistance(origin, villageA)
              distB = math.actualDistance(origin, villageB)
  
              return distA - distB
          })
      }
  
      /**
       * Order:
       * - Barbarian villages.
       * - Own villages.
       * - Tribe villages.
       *
       * @param {VillageModel} origin
       * @param {Function} callback
       */
      var closestNonHostileVillage = function (origin, callback) {
          var size = 25
          var sectors
          var targets
          var possibleTargets
          var closestTargets
          var barbs
          var own
          var tribe
          var x
          var y
          var tribeId
          var playerId
          var loads
          var index = 0
  
          if (mapData.hasTownDataInChunk(origin.x, origin.y)) {
              sectors = mapData.loadTownData(origin.x, origin.y, size, size, size)
              targets = []
              possibleTargets = []
              closestTargets
              barbs = []
              own = []
              tribe = []
              tribeId = modelDataService.getSelectedCharacter().getTribeId()
              playerId = modelDataService.getSelectedCharacter().getId()
  
              sectors.forEach(function (sector) {
                  for (x in sector.data) {
                      for (y in sector.data[x]) {
                          targets.push(sector.data[x][y])
                      }
                  }
              })
  
  
              barbs = targets.filter(function (target) {
                  return target.character_id === null && target.id > 0
              })
  
              own = targets.filter(function (target) {
                  return target.character_id === playerId && origin.id !== target.id
              })
  
              if (tribeId) {
                  tribe = targets.filter(function (target) {
                      return tribeId && target.tribe_id === tribeId
                  })
              }
  
              if (barbs.length) {
                  closestTargets = sortByDistance(barbs, origin)
              } else if (own.length) {
                  closestTargets = sortByDistance(own, origin)
              } else if (tribe.length) {
                  closestTargets = sortByDistance(tribe, origin)
              } else {
                  return callback(false)
              }
  
              return callback(closestTargets[0])
          }
          
          loads = convert.scaledGridCoordinates(origin.x, origin.y, size, size, size)
  
          mapData.loadTownDataAsync(origin.x, origin.y, size, size, function () {
              if (++index === loads.length) {
                  closestNonHostileVillage(origin, callback)
              }
          })
      }
  
      /**
       * @param {Object} data The data-object from the backend
       */
      var onOverviewIncomming = function (data) {
          var i
  
          commands = data.commands
  
          for (i = 0; i < commands.length; i++) {
              overviewService.formatCommand(commands[i])
              commands[i].slowestUnit = getSlowestUnit(commands[i])
          }
  
          commands = commands.filter(function (command) {
              return filters[FILTER_TYPES.INCOMING_UNITS][command.slowestUnit]
          })
  
          eventQueue.trigger(eventTypeProvider.ATTACK_VIEW_COMMANDS_LOADED, [commands])
      }
  
      var attackView = {}
  
      attackView.loadCommands = function () { 
          var incomingCommands = globalInfoModel.getCommandListModel().getIncomingCommands().length
          var count = incomingCommands > 25 ? incomingCommands : 25
  
          socketService.emit(routeProvider.OVERVIEW_GET_INCOMING, {
              'count': count,
              'offset': 0,
              'sorting': sorting.column,
              'reverse': sorting.reverse ? 1 : 0,
              'groups': [],
              'command_types': filterParams[FILTER_TYPES.COMMAND_TYPES],
              'villages': filterParams[FILTER_TYPES.VILLAGE]
          }, onOverviewIncomming)
      }
  
      attackView.getCommands = function () {
          return commands
      }
  
      attackView.getFilters = function () {
          return filters
      }
  
      attackView.getSortings = function () {
          return sorting
      }
  
      /**
       * Toggles the given filter.
       *
       * @param {string} type The category of the filter (see FILTER_TYPES)
       * @param {string} opt_filter The filter to be toggled.
       */
      attackView.toggleFilter = function (type, opt_filter) {
          if (!opt_filter) {
              filters[type] = !filters[type]
          } else {
              filters[type][opt_filter] = !filters[type][opt_filter]
          }
  
          // format filters for the backend
          formatFilters()
          Lockr.set(STORAGE_KEYS.FILTERS, filters)
          attackView.loadCommands()
      }
  
      attackView.toggleSorting = function (newColumn) {
          if (newColumn === sorting.column) {
              sorting.reverse = !sorting.reverse
          } else {
              sorting.column = newColumn
              sorting.reverse = false
          }
  
          attackView.loadCommands()
      }
  
      /**
       * Set an automatic command with all units from the village
       * and start the CommandQueue module if it's disabled.
       *
       * @param {Object} command Data of the command like origin, target.
       * @param {String} date Date that the command has to leave.
       */
      attackView.setCommander = function (command, date) {
          var origin
          var target
          var type
  
          closestNonHostileVillage(command.targetVillage, function (closestVillage) {
              origin = command.targetVillage
              target = closestVillage
              type = target.character_id === null ? 'attack' : 'support'
              
              commandQueue.addCommand({
                  origin: origin,
                  target: target,
                  date: date,
                  dateType: 'out',
                  units: {
                      spear: '*',
                      sword: '*',
                      axe: '*',
                      archer: '*',
                      light_cavalry: '*',
                      mounted_archer: '*',
                      heavy_cavalry: '*',
                      ram: '*',
                      catapult: '*',
                      snob: '*',
                      knight: '*',
                      doppelsoldner: '*',
                      trebuchet: '*'
                  },
                  officers: {},
                  type: type,
                  catapultTarget: 'wall'
              })
  
              if (!commandQueue.isRunning()) {
                  commandQueue.start()
              }
          })
      }
  
      attackView.commandQueueEnabled = function () {
          return commandQueueEnabled
      }
  
      attackView.isInitialized = function () {
          return initialized
      }
  
      attackView.init = function () {
          var defaultFilters
          var i
  
          for (i = 0; i < UNIT_SPEED_ORDER.length; i++) {
              INCOMING_UNITS_FILTER[UNIT_SPEED_ORDER[i]] = true
          }
  
          for (i in COMMAND_TYPES) {
              COMMAND_TYPES_FILTER[COMMAND_TYPES[i]] = true
          }
  
          try {
              commandQueue = require('two/commandQueue')
              commandQueueEnabled = !!commandQueue
          } catch (e) {}
  
          defaultFilters = {}
          defaultFilters[FILTER_TYPES.COMMAND_TYPES] = angular.copy(COMMAND_TYPES_FILTER)
          defaultFilters[FILTER_TYPES.INCOMING_UNITS] = angular.copy(INCOMING_UNITS_FILTER)
          defaultFilters[FILTER_TYPES.VILLAGE] = false
  
          initialized = true
          globalInfoModel = modelDataService.getSelectedCharacter().getGlobalInfo()
          filters = Lockr.get(STORAGE_KEYS.FILTERS, defaultFilters, true)
  
          ready(function () {
              formatFilters()
  
              $rootScope.$on(eventTypeProvider.COMMAND_INCOMING, onCommandIncomming)
              $rootScope.$on(eventTypeProvider.COMMAND_CANCELLED, onCommandCancelled)
              $rootScope.$on(eventTypeProvider.MAP_SELECTED_VILLAGE, onVillageSwitched)
              $rootScope.$on(eventTypeProvider.VILLAGE_NAME_CHANGED, onVillageNameChanged)
              $rootScope.$on(eventTypeProvider.COMMAND_IGNORED, onCommandIgnored)
  
              attackView.loadCommands()
          }, ['initial_village'])
      }
  
      return attackView
  })
  
  define('two/attackView/columnTypes', [], function () {
      return {
          'ORIGIN_VILLAGE': 'origin_village_name',
          'COMMAND_TYPE': 'command_type',
          'TARGET_VILLAGE': 'target_village_name',
          'TIME_COMPLETED': 'time_completed',
          'ORIGIN_CHARACTER': 'origin_character_name'
      }
  })
  
  define('two/attackView/commandTypes', [], function () {
      return {
          'ATTACK': 'attack',
          'SUPPORT': 'support',
          'RELOCATE': 'relocate'
      }
  })
  
  define('two/attackView/events', [], function () {
      angular.extend(eventTypeProvider, {
          ATTACK_VIEW_FILTERS_CHANGED: 'attack_view_filters_changed',
          ATTACK_VIEW_SORTING_CHANGED: 'attack_view_sorting_changed',
          ATTACK_VIEW_COMMAND_CANCELLED: 'attack_view_command_cancelled',
          ATTACK_VIEW_COMMAND_IGNORED: 'attack_view_command_ignored',
          ATTACK_VIEW_VILLAGE_RENAMED: 'attack_view_village_renamed',
          ATTACK_VIEW_COMMANDS_LOADED: 'attack_view_commands_loaded'
      })
  })
  
  define('two/attackView/filterTypes', [], function () {
      return {
          'COMMAND_TYPES' : 'command_types',
          'VILLAGE' : 'village',
          'INCOMING_UNITS' : 'incoming_units'
      }
  })
  
  define('two/attackView/ui', [
      'two/ui',
      'two/ui/button',
      'two/attackView',
      'two/EventScope',
      'two/utils',
      'two/attackView/columnTypes',
      'two/attackView/commandTypes',
      'two/attackView/filterTypes',
      'two/attackView/unitSpeedOrder',
      'conf/unitTypes',
      'queues/EventQueue',
      'helper/time',
      'battlecat'
  ], function (
      interfaceOverflow,
      FrontButton,
      attackView,
      EventScope,
      utils,
      COLUMN_TYPES,
      COMMAND_TYPES,
      FILTER_TYPES,
      UNIT_SPEED_ORDER,
      UNIT_TYPES,
      eventQueue,
      timeHelper,
      $
  ) {
      var $scope
      var textObject = 'attack_view'
      var textObjectCommon = 'common'
      var eventScope
  
      var nowSeconds = function () {
          return Date.now() / 1000
      }
  
      var copyTimeModal = function (time) {
          var modalScope = $rootScope.$new()
          modalScope.text = $filter('readableDateFilter')(time * 1000, $rootScope.loc.ale, $rootScope.GAME_TIMEZONE, $rootScope.GAME_TIME_OFFSET, 'H:mm:ss:sss dd/MM/yyyy')
          modalScope.title = $filter('i18n')('copy', $rootScope.loc.ale, textObject)
          windowManagerService.getModal('!twoverflow_attack_view_show_text_modal', modalScope)
      }
  
      var removeTroops = function (command) {
          var formatedDate = $filter('readableDateFilter')((command.time_completed - 10) * 1000, $rootScope.loc.ale, $rootScope.GAME_TIMEZONE, $rootScope.GAME_TIME_OFFSET, 'H:mm:ss:sss dd/MM/yyyy')
          attackView.setCommander(command, formatedDate)
      }
  
      var switchWindowSize = function () {
          var $window = $('#two-attack-view').parent()
          var $wrapper = $('#wrapper')
  
          $window.toggleClass('fullsize')
          $wrapper.toggleClass('window-fullsize')
      }
  
      var updateVisibileCommands = function () {
          var offset = $scope.pagination.offset
          var limit = $scope.pagination.limit
  
          $scope.visibleCommands = $scope.commands.slice(offset, offset + limit)
          $scope.pagination.count = $scope.commands.length
      }
  
      var checkCommands = function () {
          var now = Date.now()
          var i
          
          for (i = 0; i < $scope.commands.length; i++) {
              if ($scope.commands[i].model.percent(now) === 100) {
                  $scope.commands.splice(i, 1)
              }
          }
  
          updateVisibileCommands()
      }
  
      // scope functions
  
      var toggleFilter = function (type, _filter) {
          attackView.toggleFilter(type, _filter)
          $scope.filters = attackView.getFilters()
      }
  
      var toggleSorting = function (column) {
          attackView.toggleSorting(column)
          $scope.sorting = attackView.getSortings()
      }
  
      var eventHandlers = {
          updateCommands: function () {
              $scope.commands = attackView.getCommands()
          },
          onVillageSwitched: function () {
              $scope.selectedVillageId = modelDataService.getSelectedVillage().getId()
          }
      }
  
      var init = function () {
          var opener = new FrontButton('AttackView', {
              classHover: false,
              classBlur: false,
              onClick: buildWindow
          })
  
          interfaceOverflow.addTemplate('twoverflow_attack_view_main', `<div id="two-attack-view" class="win-content two-window"><header class="win-head"><h2>AttackView</h2><ul class="list-btn"><li><a href="#" class="size-34x34 btn-orange icon-26x26-double-arrow" ng-click="switchWindowSize()"></a></li><li><a href="#" class="size-34x34 btn-red icon-26x26-close" ng-click="closeWindow()"></a></li></ul></header><div class="win-main" scrollbar=""><div class="box-paper"><div class="scroll-wrap rich-text"><div class="filters"><table class="tbl-border-light"><tbody><tr><th>{{ 'village' | i18n:loc.ale:textObjectCommon }}</th></tr><tr><td><div class="box-border-dark icon" ng-class="{'active': filters[FILTER_TYPES.VILLAGE]}" ng-click="toggleFilter(FILTER_TYPES.VILLAGE)" tooltip="" tooltip-content="{{ 'current_only_tooltip' | i18n:loc.ale:textObject }}"><span class="icon-34x34-village-info icon-bg-black"></span></div></td></tr></tbody></table><table class="tbl-border-light"><tbody><tr><th>{{ 'filter_types' | i18n:loc.ale:textObject }}</th></tr><tr><td><div class="box-border-dark icon" ng-class="{'active': filters[FILTER_TYPES.COMMAND_TYPES][COMMAND_TYPES.ATTACK]}" ng-click="toggleFilter(FILTER_TYPES.COMMAND_TYPES, COMMAND_TYPES.ATTACK)" tooltip="" tooltip-content="{{ 'filter_show_attacks_tooltip' | i18n:loc.ale:textObject }}"><span class="icon-34x34-attack icon-bg-black"></span></div><div class="box-border-dark icon" ng-class="{'active': filters[FILTER_TYPES.COMMAND_TYPES][COMMAND_TYPES.SUPPORT]}" ng-click="toggleFilter(FILTER_TYPES.COMMAND_TYPES, COMMAND_TYPES.SUPPORT)" tooltip="" tooltip-content="{{ 'filter_show_supports_tooltip' | i18n:loc.ale:textObject }}"><span class="icon-34x34-support icon-bg-black"></span></div><div class="box-border-dark icon" ng-class="{'active': filters[FILTER_TYPES.COMMAND_TYPES][COMMAND_TYPES.RELOCATE]}" ng-click="toggleFilter(FILTER_TYPES.COMMAND_TYPES, COMMAND_TYPES.RELOCATE)" tooltip="" tooltip-content="{{ 'filter_show_relocations_tooltip' | i18n:loc.ale:textObject }}"><span class="icon-34x34-relocate icon-bg-black"></span></div></td></tr></tbody></table><table class="tbl-border-light"><tbody><tr><th>{{ 'filter_incoming_units' | i18n:loc.ale:textObject }}</th></tr><tr><td><div ng-repeat="unit in ::UNIT_SPEED_ORDER" class="box-border-dark icon" ng-class="{'active': filters[FILTER_TYPES.INCOMING_UNITS][unit]}" ng-click="toggleFilter(FILTER_TYPES.INCOMING_UNITS, unit)" tooltip="" tooltip-content="{{ unit | i18n:loc.ale:'unit_names' }}"><span class="icon-34x34-unit-{{ unit }} icon-bg-black"></span></div></td></tr></tbody></table></div><div class="page-wrap" pagination="pagination"></div><p class="text-center" ng-show="!visibleCommands.length">{{ 'no_incoming' | i18n:loc.ale:textObject }}</p><table class="tbl-border-light commands-table" ng-show="visibleCommands.length"><colgroup><col width="8%"><col width="14%"><col><col><col width="4%"><col width="15%"><col width="11%"></colgroup><thead class="sorting"><tr><th ng-click="toggleSorting(COLUMN_TYPES.COMMAND_TYPE)" tooltip="" tooltip-content="{{ 'command_type_tooltip' | i18n:loc.ale:textObject }}">{{ 'command_type' | i18n:loc.ale:textObject }} <span class="arrow" ng-show="sorting.column == COLUMN_TYPES.COMMAND_TYPE" ng-class="{'icon-26x26-normal-arrow-down': sorting.reverse, 'icon-26x26-normal-arrow-up': !sorting.reverse}"></span></th><th ng-click="toggleSorting(COLUMN_TYPES.ORIGIN_CHARACTER)">{{ 'player' | i18n:loc.ale:textObjectCommon }} <span class="arrow" ng-show="sorting.column == COLUMN_TYPES.ORIGIN_CHARACTER" ng-class="{'icon-26x26-normal-arrow-down': sorting.reverse, 'icon-26x26-normal-arrow-up': !sorting.reverse}"></span></th><th ng-click="toggleSorting(COLUMN_TYPES.ORIGIN_VILLAGE)">{{ 'origin' | i18n:loc.ale:textObjectCommon }} <span class="arrow" ng-show="sorting.column == COLUMN_TYPES.ORIGIN_VILLAGE" ng-class="{'icon-26x26-normal-arrow-down': sorting.reverse, 'icon-26x26-normal-arrow-up': !sorting.reverse}"></span></th><th ng-click="toggleSorting(COLUMN_TYPES.TARGET_VILLAGE)">{{ 'target' | i18n:loc.ale:textObjectCommon }} <span class="arrow" ng-show="sorting.column == COLUMN_TYPES.TARGET_VILLAGE" ng-class="{'icon-26x26-normal-arrow-down': sorting.reverse, 'icon-26x26-normal-arrow-up': !sorting.reverse}"></span></th><th tooltip="" tooltip-content="{{ 'slowest_unit_tooltip' | i18n:loc.ale:textObject }}">{{ 'slowest_unit' | i18n:loc.ale:textObject }}</th><th ng-click="toggleSorting(COLUMN_TYPES.TIME_COMPLETED)">{{ 'arrive' | i18n:loc.ale:textObjectCommon }} <span class="arrow" ng-show="sorting.column == COLUMN_TYPES.TIME_COMPLETED" ng-class="{'icon-26x26-normal-arrow-down': sorting.reverse, 'icon-26x26-normal-arrow-up': !sorting.reverse}"></span></th><th>{{ 'actions' | i18n:loc.ale:textObject }}</th></tr></thead><tbody><tr ng-repeat="command in visibleCommands" class="{{ command.command_type }}" ng-class="{'snob': command.slowestUnit === UNIT_TYPES.SNOB}"><td><span class="icon-20x20-{{ command.command_type }}"></span></td><td ng-click="openCharacterProfile(command.originCharacter.id)" class="character"><span class="name">{{ command.originCharacter.name }}</span></td><td ng-class="{'selected': command.originVillage.id === selectedVillageId}" class="village"><span class="name" ng-click="openVillageInfo(command.originVillage.id)">{{ command.originVillage.name }}</span> <span class="coords" ng-click="jumpToVillage(command.originVillage.x, command.originVillage.y)">({{ command.originVillage.x }}|{{ command.originVillage.y }})</span></td><td ng-class="{'selected': command.targetVillage.id === selectedVillageId}" class="village"><span class="name" ng-click="openVillageInfo(command.targetVillage.id)">{{ command.targetVillage.name }}</span> <span class="coords" ng-click="jumpToVillage(command.targetVillage.x, command.targetVillage.y)">({{ command.targetVillage.x }}|{{ command.targetVillage.y }})</span></td><td><span class="icon-20x20-unit-{{ command.slowestUnit }}"></span></td><td><div class="progress-wrapper" tooltip="" tooltip-content="{{ command.model.arrivalTime() | readableDateFilter:loc.ale:GAME_TIMEZONE:GAME_TIME_OFFSET }}"><div class="progress-bar" ng-style="{width: command.model.percent() + '%'}"></div><div class="progress-text"><span>{{ command.model.countdown() }}</span></div></div></td><td><a ng-click="copyTimeModal(command.time_completed)" class="btn btn-orange size-20x20 icon-20x20-arrivetime" tooltip="" tooltip-content="{{ 'commands_copy_arrival_tooltip' | i18n:loc.ale:textObject }}"></a> <a ng-click="copyTimeModal(command.time_completed + (command.time_completed - command.time_start))" class="btn btn-red size-20x20 icon-20x20-backtime" tooltip="" tooltip-content="{{ 'commands_copy_backtime_tooltip' | i18n:loc.ale:textObject }}"></a> <a ng-if="commandQueueEnabled" ng-click="removeTroops(command)" class="btn btn-orange size-20x20 icon-20x20-units-outgoing" tooltip="" tooltip-content="{{ 'commands_set_remove_tooltip' | i18n:loc.ale:textObject }}"></a></td></tr></tbody></table><div class="page-wrap" pagination="pagination"></div></div></div></div></div>`)
          interfaceOverflow.addTemplate('twoverflow_attack_view_show_text_modal', `<div id="show-text-modal" class="win-content"><header class="win-head"><h3>{{ title }}</h3><ul class="list-btn sprite"><li><a href="#" class="btn-red icon-26x26-close" ng-click="closeWindow()"></a></li></ul></header><div class="win-main" scrollbar=""><div class="box-paper"><div class="scroll-wrap"><form ng-submit="closeWindow()"><input type="text" class="input-border text-center" ng-model="text"></form></div></div></div><footer class="win-foot sprite-fill"><ul class="list-btn list-center"><li><a href="#" class="btn-green btn-border" ng-click="closeWindow()">OK</a></li></ul></footer></div>`)
          interfaceOverflow.addStyle('#two-attack-view table.commands-table{table-layout:fixed;font-size:13px;margin-bottom:10px}#two-attack-view table.commands-table th{text-align:center;padding:0px}#two-attack-view table.commands-table td{padding:1px 0;min-height:initial;border:none;text-align:center}#two-attack-view table.commands-table tr.attack.snob td{background:#bb8658}#two-attack-view table.commands-table tr.support td,#two-attack-view table.commands-table tr.relocate td{background:#9c9368}#two-attack-view table.commands-table .empty td{height:32px}#two-attack-view table.commands-table .sorting .arrow{margin-top:-4px}#two-attack-view .village .coords{font-size:11px;color:#71471a}#two-attack-view .village .coords:hover{color:#ffde00;text-shadow:0 1px 0 #000}#two-attack-view .village .name:hover{color:#fff;text-shadow:0 1px 0 #000}#two-attack-view .village.selected .name{font-weight:bold}#two-attack-view .character .name:hover{color:#fff;text-shadow:1px 1px 0 #000}#two-attack-view .progress-wrapper{height:20px;margin-bottom:0}#two-attack-view .progress-wrapper .progress-text{position:absolute;width:100%;height:100%;text-align:center;z-index:10;padding:0 5px;line-height:20px;color:#f0ffc9;overflow:hidden}#two-attack-view .filters{height:95px;margin-bottom:10px}#two-attack-view .filters table{width:auto;float:left;margin:5px}#two-attack-view .filters .icon{width:38px;float:left;margin:0 6px}#two-attack-view .filters .icon.active:before{box-shadow:0 0 0 1px #000,-1px -1px 0 2px #ac9c44,0 0 0 3px #ac9c44,0 0 0 4px #000;border-radius:1px;content:"";position:absolute;width:38px;height:38px;left:-1px;top:-1px}#two-attack-view .filters td{padding:6px}#two-attack-view .icon-20x20-backtime{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAEMklEQVQ4y42US2xUdRTGf3funZn/PHqnnVdpKZZ2RCWBVESgoZogSAKKEEAlGhVNLMGg0QiJKxYudIdoTEyDj8SFGo2seDUGhEQqRHk/UimDpdAptHMr8+jM3Dv35QJbi9KEszzJ+eU753z5JKYuOQGBUpAa2SLiuPgBPBKGrZAPlSlmoQLYk4ekqUCmEHHL0pslRb7fsNwWF8L/DIz5Fanftey0oogBr65rk8HS3WC6jyY8ckfZdNtfWdX++tzGIDMabAJmArte4my/l/c//vaLoFc6jmP3iCqD41B5Mi0BId1Hk+V6ljfEQlvWL2xZoY/lKOTLGCY01tZhVLMkRJEtqzoeyUvSnN70SNZRXC1iUylDVZmszhQiDmbH9Lrgpta4mKPlCjy95D6Wrn8GAKFEEfEmdG2Qowd+4I0XFrUC7+w7eL5sCu8hdL3imaQuYFl6c9l021vjYk7Y72Xjq4/z1IaNCCVKMRckq+moiQDJ2bN48uV3GbnSx9b1ra1l0223LL05AYF/Vw4S80jyonnN6paq5YTe3LyU2rpaYrFpJGfPItlcTzI1H8R8cC38NTFiaojhSzeJJ8KNJ/4YOmP43GsTCmWLiGG5LTUBb2LuzGm3e3Ij3321m5Hey6A0AVAcPjmhQcSbuDyU5sF6e5phuS2yRWQC6Lj4x62h1vjJ3BwjlUoiYn52ffolmUtnuXj4ADu2b7/DFoN9RVQ1gAthx8U/+Sk4LiGAQtFAHzXIajpr16yiu/tX98euzyWAzrc6Abj8+1G0TIZ8uYx/xJpgjANlWfEKqjaZbIlixQQgdDHDyuULWLFisZTVdBJxQTIVA2uQ+qZ6KoU0nhqV09f+QoIxj4ThAWRVJWLZToNXUaarYR8Hdm+iZBic7N5LbmgI0xclERcAFLIVAHRtkFOHjwBwNHNryK9I/bZCXlFVIk6ZuSbukidmR1Z+/cliAHzRBjKjBTq37bz9gEAAgA+2vQjAjb4j9F6pUCga/Hzm5v6A5KRDFkXF1UnWRcRj256d/vam9zrJXT0GwGc7V+ONRwAwtTwAa9bs4ND+PTy8MMW5az7+vJ7lXKZ4IeiVjsuIgaylVxTHxf/S84+u3bh5Mbmrx/D6Y1hjGtaYBjduH9g0RonNSmH4o/T1j9JzeoBixSRbsi9ktNIuRXJ6vFVbA2ypVoiZNuay+qj62r6u1R0ee4i65Iw7rDEOnLegC4CSqwxf18b23C0cFMenF5wKJzLZfLDtuW/4pWt1Ry6XY8/ug8jRB6gN3GI0k6VtXcq9csvqtm2rTyjS+YDkpGXEgLdq/z++EhA2hYjbmMtMx7P8+4/Wbdj64U89/cP5Xlli2HGcUsAnjziulMGxbrheRu4lYH21QjSarvXQoraZbQC/nUoflzwMyx6hVz26MRVkysROQNhQ8XmqQr1XwH/rb2Du69Eebp25AAAAAElFTkSuQmCC")}#two-attack-view .icon-20x20-arrivetime{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAEW0lEQVQ4y4WUWWxUZRiGn7PMnNPOVtvODHQBSlulAUFBoQiEaBHBhCsSFaIhIe6JSyAkRkO8NpErY2KoYuINISkkRFAjEUyAUCQsBSu1BVpKZ2DmTNuZzsyZMz3L70Vbgkjqe/Ul//89//K9eSX+KyUKFcVKQopDxBNoALJE2VXJBUzyBpQA9xG9SA+DbF2vdRxrvqQqLWVHNAkITm8saKo0KBz3hqrqt32WlXkUWHoQZvlpQFbWmLZo//zj7W8ua7JRUoKSz+DOXYVrSZMfjnV/W+mTuvHcs/okIw9DFYAoBCw/DY6QX9yycemer9/p6KiQE7ilIj4vwNXBFIO3M1iFLKta4suNvLUwZzpZTxWZiEvJhMkHgYpf1+cKSazfsnHpnve2rVqYTg2xdvMrPL76JWKNNSxesYB1LyyDiQQ9fWkCmhxzkRuLZTcpVC1lOU4eEDNPDUzitJVc6eUDn6zuSAwl2PDGLqrnx9ECPob6kkxaPiLBEK1LniIaFVz/c4SAJsf6U2ZaEfZwxMOYuaVCJTWypKz68LXV7y6sigWf7thMdfMKkMOgryA2r5pYYwWBaA3FzBhFM8uiRXFOnumn/jGt0SjYl8t+MWzbFABkxSFSdkTTE3F3zkDyBnptw/2J5VMXpwq1gfT1AQ4eOIyi1AHw5II5hCp80bIjmhSHyEyP7Ak0AcFwuIKR/vy/PLVv7156T/1M4u8e9n/1HXqNRnNzjMS9AuGQBlMfF5zxKoA6U2hph5xp0nv+ErX1KVqfXctbH+yk65tOAOa1tolNm56TjIyFNVpmIl8GwBMEHnSzKkuUJUHh8vAYcihMIFQi3hAHZ4T65hq27dyKkbGI1uqS7a/mXO8F+gZGuDZ0j4nClFsU1adj2wrgyq5KTlOlwTOJ8STApVO/Y2VGAJgwSgBEa3VsfzXZZJKLvxyjWC7z8+G3CQf9+FS13nG9ueEwEUBRqmywEfrAvWLF4rqq5fmiwCvcIjuqYCTu8v5nnXQd7+bgoZ/48dduXF8F4ZpaNj0/j60bgly+YLTeNMyUYosxPUhONaBUpeq3K7G7T/Ym2pfWh5ZU1MzBX/0XV/64iVYe4+jR3QD4aqeGaWdylPNjABw9upv9X3R+9GVXwsjmrZQCiJDjOI4scjnTyZZc0ZhKJmM9PcNYlsu4CLJjez3jt65ij45jpZPYhVG8SRNFrcQc7eeZ9evIl9xI96Xh4yqAAaXoJCOW3zuRGjfNwbRob6wNbkkYxTizaDx9B0+pY93rnWdTYxPf+xQ9p0yvCRPciEtJqFpKEfZwyXaupArOYLbM+JK2lS3HDhyRbgwanO6eoPvEaWLxOixLY+WOrrP5onUI4Z2TdMeQZgtYySaGrM6VJVFfmnRjsiwHXEG8KR5p2/fpxjWv7jpyyCd7JxR8v03nY0Fidt2H+z1dcz1LFx7xlctb2gHO9wz1+CS1L2tZSabD4f+Asx7g+a0JbYJJg6lgAPgHUh4QWRIJr4EAAAAASUVORK5CYII=")}')
      }
  
      var buildWindow = function () {
          $scope = $rootScope.$new()
          $scope.textObject = textObject
          $scope.textObjectCommon = textObjectCommon
          $scope.commandQueueEnabled = attackView.commandQueueEnabled()
          $scope.commands = attackView.getCommands()
          $scope.selectedVillageId = modelDataService.getSelectedVillage().getId()
          $scope.filters = attackView.getFilters()
          $scope.sorting = attackView.getSortings()
          $scope.UNIT_TYPES = UNIT_TYPES
          $scope.FILTER_TYPES = FILTER_TYPES
          $scope.COMMAND_TYPES = COMMAND_TYPES
          $scope.UNIT_SPEED_ORDER = UNIT_SPEED_ORDER
          $scope.COLUMN_TYPES = COLUMN_TYPES
          $scope.pagination = {
              count: $scope.commands.length,
              offset: 0,
              loader: updateVisibileCommands,
              limit: storageService.getPaginationLimit()
          }
  
          // functions
          $scope.openCharacterProfile = windowDisplayService.openCharacterProfile
          $scope.openVillageInfo = windowDisplayService.openVillageInfo
          $scope.jumpToVillage = mapService.jumpToVillage
          $scope.now = nowSeconds
          $scope.copyTimeModal = copyTimeModal
          $scope.removeTroops = removeTroops
          $scope.switchWindowSize = switchWindowSize
          $scope.toggleFilter = toggleFilter
          $scope.toggleSorting = toggleSorting
  
          updateVisibileCommands()
  
          eventScope = new EventScope('twoverflow_queue_window', function onWindowClose() {
              timeHelper.timer.remove(checkCommands)
          })
          eventScope.register(eventTypeProvider.MAP_SELECTED_VILLAGE, eventHandlers.onVillageSwitched, true)
          eventScope.register(eventTypeProvider.ATTACK_VIEW_COMMANDS_LOADED, eventHandlers.updateCommands)
          eventScope.register(eventTypeProvider.ATTACK_VIEW_COMMAND_CANCELLED, eventHandlers.updateCommands)
          eventScope.register(eventTypeProvider.ATTACK_VIEW_COMMAND_IGNORED, eventHandlers.updateCommands)
          eventScope.register(eventTypeProvider.ATTACK_VIEW_VILLAGE_RENAMED, eventHandlers.updateCommands)
  
          windowManagerService.getScreenWithInjectedScope('!twoverflow_attack_view_main', $scope)
  
          timeHelper.timer.add(checkCommands)
      }
  
      return init
  })
  
  define('two/attackView/unitSpeedOrder', [
      'conf/unitTypes'
  ], function (
      UNIT_TYPES
  ) {
      return [
          UNIT_TYPES.LIGHT_CAVALRY,
          UNIT_TYPES.HEAVY_CAVALRY,
          UNIT_TYPES.AXE,
          UNIT_TYPES.SWORD,
          UNIT_TYPES.RAM,
          UNIT_TYPES.SNOB,
          UNIT_TYPES.TREBUCHET
      ]
  })
  
  require([
      'two/language',
      'two/ready',
      'two/attackView',
      'two/attackView/ui',
      'two/attackView/events'
  ], function (
      twoLanguage,
      ready,
      attackView,
      attackViewInterface
  ) {
      if (attackView.isInitialized()) {
          return false
      }
  
      ready(function () {
          twoLanguage.add('attack_view', {"cs_cz":{"attack_view":{"filter_types":"Types","filter_show_attacks_tooltip":"Show attacks","filter_show_supports_tooltip":"Show supports","filter_show_relocations_tooltip":"Show relocations","filter_incoming_units":"Incoming Units","commands_copy_arrival_tooltip":"Copy arrival date.","commands_copy_backtime_tooltip":"Copy backtime date.","commands_set_remove_tooltip":"Set a CommandQueue to remove all troops before the attack hit.","command_type_tooltip":"Command Type","slowest_unit_tooltip":"Slowest Unit","command_type":"CT","slowest_unit":"SU","actions":"Actions","no_incoming":"No commands incoming.","copy":"Copy","current_only_tooltip":"Current village only"}},"de_de":{"attack_view":{"filter_types":"Typen","filter_show_attacks_tooltip":"Zeige Angriffe","filter_show_supports_tooltip":"Zeige Unterstützungen","filter_show_relocations_tooltip":"Zeige Umsiedlungen","filter_incoming_units":"Ankommende Einheiten","commands_copy_arrival_tooltip":"Kopiere Ankunftsdatum.","commands_copy_backtime_tooltip":"Kopiere Datum der Rückkehr.","commands_set_remove_tooltip":"Erzeuge eine Befehlskette, um die Truppen vor dem Angriff rauszustellen.","command_type_tooltip":"Befehlstyp","slowest_unit_tooltip":"Langsamste Einheit","command_type":"AT","slowest_unit":"LE","actions":"Aktionen","no_incoming":"Keine eingehenden Befehle.","copy":"Copy","current_only_tooltip":"Nur aktuelles Dorf"}},"en_us":{"attack_view":{"filter_types":"Types","filter_show_attacks_tooltip":"Show attacks","filter_show_supports_tooltip":"Show supports","filter_show_relocations_tooltip":"Show relocations","filter_incoming_units":"Incoming Units","commands_copy_arrival_tooltip":"Copy arrival date.","commands_copy_backtime_tooltip":"Copy backtime date.","commands_set_remove_tooltip":"Set a CommandQueue to remove all troops before the attack hit.","command_type_tooltip":"Command Type","slowest_unit_tooltip":"Slowest Unit","command_type":"CT","slowest_unit":"SU","actions":"Actions","no_incoming":"No commands incoming.","copy":"Copy","current_only_tooltip":"Current village only"}},"fr_fr":{"attack_view":{"filter_types":"Types","filter_show_attacks_tooltip":"Montrer les attaques","filter_show_supports_tooltip":"Montrer les soutiens","filter_show_relocations_tooltip":"Montrer les relocalisations","filter_incoming_units":"Troupes entrantes","commands_copy_arrival_tooltip":"Copier la date d'arrivée.","commands_copy_backtime_tooltip":"Copier la date de retour.","commands_set_remove_tooltip":"Définissez une file d'attente pour supprimer toutes les troupes avant l'attaque.","command_type_tooltip":"Type de commande","slowest_unit_tooltip":"Troupe la plus lente","command_type":"TC","slowest_unit":"UL","actions":"Actions","no_incoming":"Pas de commandes entrante.","copy":"Copier","current_only_tooltip":"Village actuel seulement"}},"pl_pl":{"attack_view":{"filter_types":"Rodzaj","filter_show_attacks_tooltip":"Pokaż ataki","filter_show_supports_tooltip":"Pokaż wsparcia","filter_show_relocations_tooltip":"Pokaż przeniesienia","filter_incoming_units":"Nadchodzące jednostki","commands_copy_arrival_tooltip":"Kopiuj czas dotarcia.","commands_copy_backtime_tooltip":"Kopiuj czas powrotu do wioski źródłowej.","commands_set_remove_tooltip":"Wstaw rozkaz wycofania wojsk przed dotarciem ataku do Kolejki rozkazów.","command_type_tooltip":"Rodzaj","slowest_unit_tooltip":"Najwolniejsza jednostka","command_type":"Rodzaj","slowest_unit":"Co?","actions":"Dostępne akcje","no_incoming":"Brak nadchodzących wojsk.","copy":"Copy","current_only_tooltip":"Tylko aktywna wioska"}},"pt_br":{"attack_view":{"filter_types":"Tipos","filter_show_attacks_tooltip":"Mostrar ataques","filter_show_supports_tooltip":"Mostrar apoios","filter_show_relocations_tooltip":"Mostrar transferências","filter_incoming_units":"Unidades Chegando","commands_copy_arrival_tooltip":"Copiar data de chegada.","commands_copy_backtime_tooltip":"Copiar backtime.","commands_set_remove_tooltip":"Criar um comando no CommandQueue para remover todas tropas da aldeia antes do comando bater na aldeia.","command_type_tooltip":"Tipo de Comando","slowest_unit_tooltip":"Unidade mais Lenta","command_type":"TC","slowest_unit":"UL","actions":"Ações","no_incoming":"Nenhum comando chegando.","copy":"Copiar","current_only_tooltip":"Apenas aldeia selecionada"}}})
          attackView.init()
          attackViewInterface()
      })
  })
  
  define('two/autoCollector', [
      'queues/EventQueue',
      'helper/time',
      'Lockr'
  ], function (
      eventQueue,
      $timeHelper,
      Lockr
  ) {
      /**
       * Indica se o modulo já foi iniciado.
       *
       * @type {Boolean}
       */
      var initialized = false
  
      /**
       * Indica se o modulo está em funcionamento.
       *
       * @type {Boolean}
       */
      var running = false
  
      /**
       * Permite que o evento RESOURCE_DEPOSIT_JOB_COLLECTIBLE seja executado
       * apenas uma vez.
       *
       * @type {Boolean}
       */
      var recall = true
  
      /**
       * Next automatic reroll setTimeout ID.
       * 
       * @type {Number}
       */
      var nextUpdateId = 0
  
      /**
       * Inicia um trabalho.
       *
       * @param {Object} job - Dados do trabalho
       */
      var startJob = function (job) {
          socketService.emit(routeProvider.RESOURCE_DEPOSIT_START_JOB, {
              job_id: job.id
          })
      }
  
      /**
       * Coleta um trabalho.
       *
       * @param {Object} job - Dados do trabalho
       */
      var finalizeJob = function (job) {
          socketService.emit(routeProvider.RESOURCE_DEPOSIT_COLLECT, {
              job_id: job.id,
              village_id: modelDataService.getSelectedVillage().getId()
          })
      }
  
      /**
       * Força a atualização das informações do depósito.
       */
      var updateDepositInfo = function () {
          socketService.emit(routeProvider.RESOURCE_DEPOSIT_GET_INFO, {})
      }
  
      /**
       * Faz a analise dos trabalhos sempre que um evento relacionado ao depósito
       * é disparado.
       */
      var analyse = function () {
          var data
          var current
          var collectible
          var ready
          
          if (!running) {
              return false
          }
  
          data = modelDataService.getSelectedCharacter().getResourceDeposit()
  
          if (!data) {
              return false
          }
  
          current = data.getCurrentJob()
  
          if (current) {
              return false
          }
  
          collectible = data.getCollectibleJobs()
  
          if (collectible) {
              return finalizeJob(collectible.shift())
          }
  
          ready = data.getReadyJobs()
  
          if (ready) {
              return startJob(getFastestJob(ready))
          }
      }
  
      /**
       * Obtem o trabalho de menor duração.
       *
       * @param {Array} jobs - Lista de trabalhos prontos para serem iniciados.
       */
      var getFastestJob = function (jobs) {
          var sorted = jobs.sort(function (a, b) {
              return a.duration - b.duration
          })
  
          return sorted[0]
      }
  
      /**
       * Atualiza o timeout para que seja forçado a atualização das informações
       * do depósito quando for resetado.
       * Motivo: só é chamado automaticamente quando um milestone é resetado,
       * e não o diário.
       * 
       * @param {Object} data - Os dados recebidos de RESOURCE_DEPOSIT_INFO
       */
      var rerollUpdater = function (data) {
          var timeLeft = data.time_next_reset * 1000 - Date.now() + 1000
  
          clearTimeout(nextUpdateId)
          nextUpdateId = setTimeout(updateDepositInfo, timeLeft)
      }
  
      /**
       * Métodos públicos do AutoCollector.
       *
       * @type {Object}
       */
      var autoCollector = {}
  
      /**
       * Inicializa o AutoDepois, configura os eventos.
       */
      autoCollector.init = function () {
          initialized = true
  
          $rootScope.$on(eventTypeProvider.RESOURCE_DEPOSIT_JOB_COLLECTIBLE, function () {
              if (!recall || !running) {
                  return false
              }
  
              recall = false
  
              setTimeout(function () {
                  recall = true
                  analyse()
              }, 1500)
          })
  
          $rootScope.$on(eventTypeProvider.RESOURCE_DEPOSIT_JOBS_REROLLED, analyse)
          $rootScope.$on(eventTypeProvider.RESOURCE_DEPOSIT_JOB_COLLECTED, analyse)
          $rootScope.$on(eventTypeProvider.RESOURCE_DEPOSIT_INFO, function (event, data) {
              analyse()
              rerollUpdater(data)
          })
      }
  
      /**
       * Inicia a analise dos trabalhos.
       */
      autoCollector.start = function () {
          eventQueue.trigger(eventTypeProvider.AUTO_COLLECTOR_STARTED)
          running = true
          analyse()
      }
  
      /**
       * Para a analise dos trabalhos.
       */
      autoCollector.stop = function () {
          eventQueue.trigger(eventTypeProvider.AUTO_COLLECTOR_STOPPED)
          running = false
      }
  
      /**
       * Retorna se o modulo está em funcionamento.
       */
      autoCollector.isRunning = function () {
          return running
      }
  
      /**
       * Retorna se o modulo está inicializado.
       */
      autoCollector.isInitialized = function () {
          return initialized
      }
  
      return autoCollector
  })
  
  define('two/autoCollector/events', [], function () {
      angular.extend(eventTypeProvider, {
          AUTO_COLLECTOR_STARTED: 'auto_collector_started',
          AUTO_COLLECTOR_STOPPED: 'auto_collector_stopped',
          AUTO_COLLECTOR_SECONDVILLAGE_STARTED: 'auto_collector_secondvillage_started',
          AUTO_COLLECTOR_SECONDVILLAGE_STOPPED: 'auto_collector_secondvillage_stopped'
      })
  })
  
  define('two/autoCollector/ui', [
      'two/autoCollector',
      'two/ui/button',
      'two/utils',
      'queues/EventQueue'
  ], function (
      autoCollector,
      FrontButton,
      utils,
      eventQueue
  ) {
      var opener
      var textObject = 'auto_collector'
  
      var init = function () {
          opener = new FrontButton('Collector', {
              classHover: false,
              classBlur: false,
              tooltip: $filter('i18n')('description', $rootScope.loc.ale, textObject)
          })
  
          opener.click(function () {
              if (autoCollector.isRunning()) {
                  autoCollector.stop()
                  autoCollector.secondVillage.stop()
                  utils.emitNotif('success', $filter('i18n')('deactivated', $rootScope.loc.ale, textObject))
              } else {
                  autoCollector.start()
                  autoCollector.secondVillage.start()
                  utils.emitNotif('success', $filter('i18n')('activated', $rootScope.loc.ale, textObject))
              }
          })
  
          eventQueue.register(eventTypeProvider.AUTO_COLLECTOR_STARTED, function () {
              opener.$elem.classList.remove('btn-green')
              opener.$elem.classList.add('btn-red')
          })
  
          eventQueue.register(eventTypeProvider.AUTO_COLLECTOR_STOPPED, function () {
              opener.$elem.classList.remove('btn-red')
              opener.$elem.classList.add('btn-green')
          })
  
          if (autoCollector.isRunning()) {
              eventQueue.trigger(eventTypeProvider.AUTO_COLLECTOR_STARTED)
          }
  
          return opener
      }
  
      return init
  })
  
  define('two/autoCollector/secondVillage', [
      'two/autoCollector',
      'queues/EventQueue',
      'helper/time',
      'models/SecondVillageModel'
  ], function (
      autoCollector,
      eventQueue,
      $timeHelper,
      SecondVillageModel
  ) {
      var initialized = false
      var running = false
      var secondVillageService = injector.get('secondVillageService')
  
      var getRunningJob = function (jobs) {
          var now = Date.now()
          var id
  
          for (id in jobs) {
              if (jobs[id].time_started && jobs[id].time_completed) {
                  if (now < $timeHelper.server2ClientTime(jobs[id].time_completed)) {
                      return jobs[id]
                  }
              }
          }
  
          return false
      }
  
      var getCollectibleJob = function (jobs) {
          var now = Date.now()
          var id
  
          for (id in jobs) {
              if (jobs[id].time_started && jobs[id].time_completed) {
                  if ((now >= $timeHelper.server2ClientTime(jobs[id].time_completed)) && !jobs[id].collected) {
                      return id
                  }
              }
          }
  
          return false
      }
  
      var finalizeJob = function (jobId) {
          socketService.emit(routeProvider.SECOND_VILLAGE_COLLECT_JOB_REWARD, {
              village_id: modelDataService.getSelectedVillage().getId(),
              job_id: jobId
          })
      }
  
      var startJob = function (jobId, callback) {
          socketService.emit(routeProvider.SECOND_VILLAGE_START_JOB, {
              village_id: modelDataService.getSelectedVillage().getId(),
              job_id: jobId
          }, callback)
      }
  
      var getFirstJob = function (jobs) {
          var id
  
          for (id in jobs) {
              return id
          }
  
          return false
      }
  
      var updateSecondVillageInfo = function (callback) {
          var model
  
          socketService.emit(routeProvider.SECOND_VILLAGE_GET_INFO, {}, function (data) {
              model = new SecondVillageModel(data)
              modelDataService.getSelectedCharacter().setSecondVillage(model)
              callback()
          })
      }
  
      var updateAndAnalyse = function () {
          updateSecondVillageInfo(analyse)
      }
  
      var analyse = function () {
          var secondVillage = modelDataService.getSelectedCharacter().getSecondVillage()
          var current
          var completed
          var nextRun
          var collectible
          var currentDayJobs
          var collectedJobs
          var resources
          var availableJobs
          var firstJob
          var job
  
  
          if (!running || !secondVillage || !secondVillage.isAvailable()) {
              return false
          }
  
          current = getRunningJob(secondVillage.data.jobs)
  
          if (current) {
              completed = $timeHelper.server2ClientTime(current.time_completed)
              nextRun = completed - Date.now() + 1000
              setTimeout(updateAndAnalyse, nextRun)
              return false
          }
  
          collectible = getCollectibleJob(secondVillage.data.jobs)
          
          if (collectible) {
              return finalizeJob(collectible)
          }
  
          currentDayJobs = secondVillageService.getCurrentDayJobs(secondVillage.data.jobs, secondVillage.data.day)
          collectedJobs = secondVillageService.getCollectedJobs(secondVillage.data.jobs)
          resources = modelDataService.getSelectedVillage().getResources().getResources()
          availableJobs = secondVillageService.getAvailableJobs(currentDayJobs, collectedJobs, resources, [])
  
          if (availableJobs) {
              firstJob = getFirstJob(availableJobs)
  
              startJob(firstJob, function () {
                  job = availableJobs[firstJob]
                  setTimeout(updateAndAnalyse, (job.duration * 1000) + 1000)
              })
          }
      }
  
      var secondVillageCollector = {}
  
      secondVillageCollector.start = function () {
          if (!initialized) {
              return false
          }
  
          eventQueue.trigger(eventTypeProvider.AUTO_COLLECTOR_SECONDVILLAGE_STARTED)
          running = true
          updateAndAnalyse()
      }
  
      secondVillageCollector.stop = function () {
          if (!initialized) {
              return false
          }
  
          eventQueue.trigger(eventTypeProvider.AUTO_COLLECTOR_SECONDVILLAGE_STOPPED)
          running = false
      }
  
      secondVillageCollector.isRunning = function () {
          return running
      }
  
      secondVillageCollector.isInitialized = function () {
          return initialized
      }
  
      secondVillageCollector.init = function () {
          if (!secondVillageService.isFeatureActive()) {
              return false
          }
  
          initialized = true
  
          $rootScope.$on(eventTypeProvider.SECOND_VILLAGE_VILLAGE_CREATED, updateAndAnalyse)
          $rootScope.$on(eventTypeProvider.SECOND_VILLAGE_JOB_COLLECTED, updateAndAnalyse)
          $rootScope.$on(eventTypeProvider.SECOND_VILLAGE_VILLAGE_CREATED, updateAndAnalyse)
      }
  
      autoCollector.secondVillage = secondVillageCollector
  })
  
  require([
      'two/language',
      'two/ready',
      'two/autoCollector',
      'two/autoCollector/ui',
      'Lockr',
      'queues/EventQueue',
      'two/autoCollector/secondVillage',
      'two/autoCollector/events'
  ], function (
      twoLanguage,
      ready,
      autoCollector,
      autoCollectorInterface,
      Lockr,
      eventQueue
  ) {
      var STORAGE_KEYS = {
          ACTIVE: 'auto_collector_active'
      }
  
      if (autoCollector.isInitialized()) {
          return false
      }
  
      ready(function () {
          twoLanguage.add('auto_collector', {"cs_cz":{"auto_collector":{"description":"Automatické Ukládání či Přesun surovin do druhé vesnice.","activated":"Automatické ukládání aktivováno","deactivated":"Automatické ukládání deaktivováno"}},"de_de":{"auto_collector":{"description":"Automatischer Ressourcensammler / Ausbau des zweiten Dorfs.","activated":"Automatischer Resourcensammler aktiviert","deactivated":"Automatischer Resourcensammler deaktiviert"}},"en_us":{"auto_collector":{"description":"Automatic Resource Deposit/Second Village collector.","activated":"Automatic Collector activated","deactivated":"Automatic Collector deactivated"}},"fr_fr":{"auto_collector":{"description":"Dépôt de ressources automatique/Collecte second village.","activated":"Collecte automatique activée","deactivated":"Collecte automatique désactivée"}},"pl_pl":{"auto_collector":{"description":"Automatyczny kolekcjoner depozytu/drugiej wioski.","activated":"Kolekcjoner aktywowany","deactivated":"Kolekcjoner deaktywowany"}},"pt_br":{"auto_collector":{"description":"Coletor automático para Depósito de Recursos/Segunda Aldeia.","activated":"Coletor Automático ativado","deactivated":"Coletor Automático desativado"}}})
          autoCollector.init()
          autoCollector.secondVillage.init()
          autoCollectorInterface()
          
          ready(function () {
              if (Lockr.get(STORAGE_KEYS.ACTIVE, false, true)) {
                  autoCollector.start()
                  autoCollector.secondVillage.start()
              }
  
              eventQueue.register(eventTypeProvider.AUTO_COLLECTOR_STARTED, function () {
                  Lockr.set(STORAGE_KEYS.ACTIVE, true)
              })
  
              eventQueue.register(eventTypeProvider.AUTO_COLLECTOR_STOPPED, function () {
                  Lockr.set(STORAGE_KEYS.ACTIVE, false)
              })
          }, ['initial_village'])
      })
  })
  
  define('two/builderQueue', [
      'two/ready',
      'two/utils',
      'two/Settings',
      'two/builderQueue/settings',
      'two/builderQueue/settingsMap',
      'two/builderQueue/errorCodes',
      'conf/upgradeabilityStates',
      'conf/buildingTypes',
      'conf/locationTypes',
      'queues/EventQueue',
      'Lockr'
  ], function (
      ready,
      utils,
      Settings,
      SETTINGS,
      SETTINGS_MAP,
      ERROR_CODES,
      UPGRADEABILITY_STATES,
      BUILDING_TYPES,
      LOCATION_TYPES,
      eventQueue,
      Lockr
  ) {
      var buildingService = injector.get('buildingService')
      var premiumActionService = injector.get('premiumActionService')
      var buildingQueueService = injector.get('buildingQueueService')
      var initialized = false
      var running = false
      var localSettings
      var intervalCheckId
      var buildingSequenceLimit
      var ANALYSES_PER_MINUTE = 1
      var ANALYSES_PER_MINUTE_INSTANT_FINISH = 10
      var VILLAGE_BUILDINGS = {}
      var groupList
      var $player
      var logs
      var sequencesAvail = true
      var settings
      var STORAGE_KEYS = {
          LOGS: 'builder_queue_log',
          SETTINGS: 'builder_queue_settings'
      }
  
      /**
       * Loop all player villages, check if ready and init the building analyse
       * for each village.
       */
      var analyseVillages = function () {
          var groupVillages = settings.getSetting(SETTINGS.GROUP_VILLAGES)
          var villageIds = groupVillages ? groupList.getGroupVillageIds(groupVillages) : getVillageIds()
          var village
          var readyState
          var queue
  
          if (!sequencesAvail) {
              builderQueue.stop()
              return false
          }
  
          villageIds.forEach(function (villageId) {
              village = $player.getVillage(villageId)
              readyState = village.checkReadyState()
              queue = village.buildingQueue
              jobs = queue.getAmountJobs()
  
              if (jobs === queue.getUnlockedSlots()) {
                  return false
              }
  
              if (!readyState.buildingQueue || !readyState.buildings) {
                  return false
              }
  
              analyseVillageBuildings(village)
          })
      }
  
      var analyseVillagesInstantFinish = function () {
          var groupVillages = settings.getSetting(SETTINGS.GROUP_VILLAGES)
          var villageIds = groupVillages ? groupList.getGroupVillageIds(groupVillages) : getVillageIds()
          var village
          var queue
          var jobs
  
          villageIds.forEach(function (villageId) {
              village = $player.getVillage(villageId)
              queue = village.buildingQueue
  
              if (queue.getAmountJobs()) {
                  jobs = queue.getQueue()
  
                  jobs.forEach(function (job) {
                      if (buildingQueueService.canBeFinishedForFree(job, village)) {
                          premiumActionService.instantBuild(job, LOCATION_TYPES.MASS_SCREEN, true, villageId)
                      }
                  })
              }
          })
      }
  
      var initializeAllVillages = function () {
          var groupVillages = settings.getSetting(SETTINGS.GROUP_VILLAGES)
          var villageIds = groupVillages ? groupList.getGroupVillageIds(groupVillages) : getVillageIds()
          var village
  
          villageIds.forEach(function (villageId) {
              village = $player.getVillage(villageId)
  
              if (!village.isInitialized()) {
                  villageService.initializeVillage(village)
              }
          })
      }
  
      /**
       * Generate an Array with all player's village IDs.
       *
       * @return {Array}
       */
      var getVillageIds = function () {
          var ids = []
          var villages = $player.getVillages()
          var id
  
          for (id in villages) {
              ids.push(id)
          }
  
          return ids
      }
  
      /**
       * Loop all village buildings, start build job if available.
       *
       * @param {VillageModel} village
       */
      var analyseVillageBuildings = function (village) {
          var buildingLevels = angular.copy(village.buildingData.getBuildingLevels())
          var currentQueue = village.buildingQueue.getQueue()
          var sequence = angular.copy(VILLAGE_BUILDINGS)
          var now
          var logData
          var sequences = settings.getSetting(SETTINGS.BUILDING_SEQUENCES)
          var activeSequenceId = settings.getSetting(SETTINGS.ACTIVE_SEQUENCE)
          var activeSequence = sequences[activeSequenceId]
  
          currentQueue.forEach(function (job) {
              buildingLevels[job.building]++
          })
  
          if (checkVillageBuildingLimit(buildingLevels)) {
              return false
          }
  
          activeSequence.some(function (buildingName) {
              if (++sequence[buildingName] > buildingLevels[buildingName]) {
                  buildingService.compute(village)
  
                  upgradeBuilding(village, buildingName, function (jobAdded, data) {
                      if (jobAdded) {
                          if (!data.job) {
                              return false
                          }
  
                          now = Date.now()
                          logData = [
                              {
                                  x: village.getX(),
                                  y: village.getY(),
                                  name: village.getName(),
                                  id: village.getId()
                              },
                              data.job.building,
                              data.job.level,
                              now
                          ]
  
                          eventQueue.trigger(eventTypeProvider.BUILDER_QUEUE_JOB_STARTED, logData)
                          logs.unshift(logData)
                          Lockr.set(STORAGE_KEYS.LOGS, logs)
                      }
                  })
  
                  return true
              }
          })
      }
  
      /**
       * Init a build job
       *
       * @param {VillageModel} village
       * @param {String} buildingName - Building to be build.
       * @param {Function} callback
       */
      var upgradeBuilding = function (village, buildingName, callback) {
          var buildingData = village.getBuildingData().getDataForBuilding(buildingName)
  
          if (buildingData.upgradeability === UPGRADEABILITY_STATES.POSSIBLE) {
              socketService.emit(routeProvider.VILLAGE_UPGRADE_BUILDING, {
                  building: buildingName,
                  village_id: village.getId(),
                  location: LOCATION_TYPES.MASS_SCREEN,
                  premium: false
              }, function (data, event) {
                  callback(true, data)
              })
          } else {
              callback(false)
          }
      }
  
      /**
       * Check if all buildings from the sequence already reached
       * the specified level.
       *
       * @param {Object} buildingLevels - Current buildings level from the village.
       * @return {Boolean} True if the levels already reached the limit.
       */
      var checkVillageBuildingLimit = function (buildingLevels) {
          for (var buildingName in buildingLevels) {
              if (buildingLevels[buildingName] < buildingSequenceLimit[buildingName]) {
                  return false
              }
          }
  
          return true
      }
  
      /**
       * Check if the building sequence is valid by analysing if the
       * buildings exceed the maximum level.
       *
       * @param {Array} sequence
       * @return {Boolean}
       */
      var validSequence = function (sequence) {
          var sequence = angular.copy(VILLAGE_BUILDINGS)
          var buildingData = modelDataService.getGameData().getBuildings()
          var building
          var i
  
          for (i = 0; i < sequence.length; i++) {
              building = sequence[i]
  
              if (++sequence[building] > buildingData[building].max_level) {
                  return false
              }
          }
  
          return true
      }
  
      /**
       * Get the level max for each building.
       *
       * @param {String} sequenceId
       * @return {Object} Maximum level for each building.
       */
      var getSequenceLimit = function (sequenceId) {
          var sequences = settings.getSetting(SETTINGS.BUILDING_SEQUENCES)
          var sequence = sequences[sequenceId]
          var sequenceLimit = angular.copy(VILLAGE_BUILDINGS)
  
          sequence.forEach(function (buildingName) {
              sequenceLimit[buildingName]++
          })
  
          return sequenceLimit
      }
  
      var builderQueue = {}
  
      builderQueue.start = function () {
          if (!sequencesAvail) {
              eventQueue.trigger(eventTypeProvider.BUILDER_QUEUE_NO_SEQUENCES)
              return false
          }
  
          running = true
          intervalCheckId = setInterval(analyseVillages, 60000 / ANALYSES_PER_MINUTE)
          intervalInstantCheckId = setInterval(analyseVillagesInstantFinish, 60000 / ANALYSES_PER_MINUTE_INSTANT_FINISH)
          
          ready(function () {
              initializeAllVillages()
              analyseVillages()
              analyseVillagesInstantFinish()
          }, ['all_villages_ready'])
  
          eventQueue.trigger(eventTypeProvider.BUILDER_QUEUE_START)
      }
  
      builderQueue.stop = function () {
          running = false
          clearInterval(intervalCheckId)
          clearInterval(intervalInstantCheckId)
          eventQueue.trigger(eventTypeProvider.BUILDER_QUEUE_STOP)
      }
  
      builderQueue.isRunning = function () {
          return running
      }
  
      builderQueue.isInitialized = function () {
          return initialized
      }
  
      builderQueue.getSettings = function () {
          return settings
      }
  
      builderQueue.getLogs = function () {
          return logs
      }
  
      builderQueue.clearLogs = function () {
          logs = []
          Lockr.set(STORAGE_KEYS.LOGS, logs)
          eventQueue.trigger(eventTypeProvider.BUILDER_QUEUE_CLEAR_LOGS)
      }
  
      builderQueue.addBuildingSequence = function (id, sequence) {
          var sequences = settings.getSetting(SETTINGS.BUILDING_SEQUENCES)
  
          if (id in sequences) {
              return ERROR_CODES.SEQUENCE_EXISTS
          }
  
          if (!angular.isArray(sequence)) {
              return ERROR_CODES.SEQUENCE_INVALID
          }
  
          sequences[id] = sequence
          settings.setSetting(SETTINGS.BUILDING_SEQUENCES, sequences, {
              quiet: true
          })
          eventQueue.trigger(eventTypeProvider.BUILDER_QUEUE_BUILDING_SEQUENCES_ADDED, id)
  
          return true
      }
  
      builderQueue.updateBuildingSequence = function (id, sequence) {
          var sequences = settings.getSetting(SETTINGS.BUILDING_SEQUENCES)
  
          if (!(id in sequences)) {
              return ERROR_CODES.SEQUENCE_NO_EXISTS
          }
  
          if (!angular.isArray(sequence) || !validSequence(sequence)) {
              return ERROR_CODES.SEQUENCE_INVALID
          }
  
          sequences[id] = sequence
          settings.setSetting(SETTINGS.BUILDING_SEQUENCES, sequences, {
              quiet: true
          })
          eventQueue.trigger(eventTypeProvider.BUILDER_QUEUE_BUILDING_SEQUENCES_UPDATED, id)
  
          return true
      }
  
      builderQueue.removeSequence = function (id) {
          var sequences = settings.getSetting(SETTINGS.BUILDING_SEQUENCES)
  
          if (!(id in sequences)) {
              return ERROR_CODES.SEQUENCE_NO_EXISTS
          }
  
          delete sequences[id]
          settings.setSetting(SETTINGS.BUILDING_SEQUENCES, sequences, {
              quiet: true
          })
          eventQueue.trigger(eventTypeProvider.BUILDER_QUEUE_BUILDING_SEQUENCES_REMOVED, id)
      }
  
      builderQueue.init = function () {
          var buildingName
          var village
  
          initialized = true
          logs = Lockr.get(STORAGE_KEYS.LOGS, [], true)
          $player = modelDataService.getSelectedCharacter()
          groupList = modelDataService.getGroupList()
          
          settings = new Settings({
              settingsMap: SETTINGS_MAP,
              storageKey: STORAGE_KEYS.SETTINGS
          })
  
          settings.onSettingsChange(function (changes, update, opt) {
              if (!opt.quiet) {
                  eventQueue.trigger(eventTypeProvider.BUILDER_QUEUE_SETTINGS_CHANGE)
              }
          })
  
          for (buildingName in BUILDING_TYPES) {
              VILLAGE_BUILDINGS[BUILDING_TYPES[buildingName]] = 0
          }
  
          sequencesAvail = Object.keys(settings.getSetting(SETTINGS.BUILDING_SEQUENCES)).length
          buildingSequenceLimit = sequencesAvail ? getSequenceLimit(settings.getSetting(SETTINGS.ACTIVE_SEQUENCE)) : false
  
          $rootScope.$on(eventTypeProvider.BUILDING_LEVEL_CHANGED, function (event, data) {
              if (!running) {
                  return false
              }
  
              setTimeout(function () {
                  village = $player.getVillage(data.village_id)
                  analyseVillageBuildings(village)
              }, 1000)
          })
      }
  
      return builderQueue
  })
  
  define('two/builderQueue/defaultOrders', [
      'conf/buildingTypes'
  ], function (
      BUILDING_TYPES
  ) {
      var defaultSequences = {}
      
      var shuffle = function (array) {
          array.sort(() => Math.random() - 0.5)
      }
  
      var parseSequence = function (rawSequence) {
          var i
          var j
          var item
          var parsed = []
  
          for (i = 0; i < rawSequence.length; i++) {
              item = rawSequence[i]
  
              if (angular.isArray(item)) {
                  shuffle(item)
                  parsed = parsed.concat(item)
              } else {
                  parsed.push(item)
              }
          }
  
          return parsed
      }
  
      var parseSequences = function (rawSequences) {
          var i
          var parsed = {}
  
          for (i in rawSequences) {
              if (rawSequences.hasOwnProperty(i)) {
                  parsed[i] = parseSequence(rawSequences[i])
              }
          }
  
          return parsed
      }
  
      defaultSequences['Essential'] = [
          BUILDING_TYPES.HEADQUARTER, // 1
          BUILDING_TYPES.FARM, // 1
          BUILDING_TYPES.WAREHOUSE, // 1
          BUILDING_TYPES.RALLY_POINT, // 1
          BUILDING_TYPES.BARRACKS, // 1
          [
              // Quest: The Resources
              BUILDING_TYPES.TIMBER_CAMP, // 1
              BUILDING_TYPES.TIMBER_CAMP, // 2
              BUILDING_TYPES.CLAY_PIT, // 1
              BUILDING_TYPES.IRON_MINE, // 1
  
              BUILDING_TYPES.HEADQUARTER, // 2
              BUILDING_TYPES.RALLY_POINT, // 2
          ],
          [
              // Quest: First Steps
              BUILDING_TYPES.FARM, // 2
              BUILDING_TYPES.WAREHOUSE, // 2
              
              // Quest: Laying Down Foundation
              BUILDING_TYPES.CLAY_PIT, // 2
              BUILDING_TYPES.IRON_MINE, // 2
          ],
          [
              // Quest: More Resources
              BUILDING_TYPES.TIMBER_CAMP, // 3
              BUILDING_TYPES.CLAY_PIT, // 3
              BUILDING_TYPES.IRON_MINE, // 3
              
              // Quest: Resource Building
              BUILDING_TYPES.WAREHOUSE, // 3
              BUILDING_TYPES.TIMBER_CAMP, // 4
              BUILDING_TYPES.CLAY_PIT, // 4
              BUILDING_TYPES.IRON_MINE, // 4
          ],
          [
              // Quest: Get an Overview
              BUILDING_TYPES.WAREHOUSE, // 4
              BUILDING_TYPES.TIMBER_CAMP, // 5
              BUILDING_TYPES.CLAY_PIT, // 5
              BUILDING_TYPES.IRON_MINE, // 5
  
              // Quest: Capital
              BUILDING_TYPES.FARM, // 3
              BUILDING_TYPES.WAREHOUSE, // 5
              BUILDING_TYPES.HEADQUARTER, // 3
          ],
          [
              // Quest: The Hero
              BUILDING_TYPES.STATUE, // 1
  
              // Quest: Resource Expansions
              BUILDING_TYPES.TIMBER_CAMP, // 6
              BUILDING_TYPES.CLAY_PIT, // 6
              BUILDING_TYPES.IRON_MINE, // 6
          ],
          [
              // Quest: Military
              BUILDING_TYPES.BARRACKS, // 2
  
              // Quest: The Hospital
              BUILDING_TYPES.HEADQUARTER, // 4
              BUILDING_TYPES.TIMBER_CAMP, // 7
              BUILDING_TYPES.CLAY_PIT, // 7
              BUILDING_TYPES.IRON_MINE, // 7
              BUILDING_TYPES.FARM, // 4
              BUILDING_TYPES.HOSPITAL, // 1
          ],
          [
              // Quest: Resources
              BUILDING_TYPES.TIMBER_CAMP, // 8
              BUILDING_TYPES.CLAY_PIT, // 8
              BUILDING_TYPES.IRON_MINE, // 8
  
              // Quest: The Wall
              BUILDING_TYPES.WAREHOUSE, // 6
              BUILDING_TYPES.HEADQUARTER, // 5
              BUILDING_TYPES.WALL, // 1
          ],
          [
              // Quest: Village Improvements
              BUILDING_TYPES.TIMBER_CAMP, // 9
              BUILDING_TYPES.CLAY_PIT, // 9
              BUILDING_TYPES.IRON_MINE, // 9
              BUILDING_TYPES.TIMBER_CAMP, // 10
              BUILDING_TYPES.CLAY_PIT, // 10
              BUILDING_TYPES.IRON_MINE, // 10
              BUILDING_TYPES.FARM, // 5
          ],
          BUILDING_TYPES.FARM, // 6
          BUILDING_TYPES.FARM, // 7
          [
              // Quest: Hard work
              BUILDING_TYPES.TIMBER_CAMP, // 11
              BUILDING_TYPES.CLAY_PIT, // 11
              BUILDING_TYPES.IRON_MINE, // 11
              BUILDING_TYPES.TIMBER_CAMP, // 12
              BUILDING_TYPES.CLAY_PIT, // 12
              BUILDING_TYPES.IRON_MINE, // 12
          ],
          [
              // Quest: The way of defence
              BUILDING_TYPES.BARRACKS, // 3
  
              BUILDING_TYPES.WAREHOUSE, // 7
              BUILDING_TYPES.WAREHOUSE, // 8
              BUILDING_TYPES.FARM, // 8
              BUILDING_TYPES.WAREHOUSE, // 9
              BUILDING_TYPES.WAREHOUSE, // 10
          ],
          [
              // Quest: Market Barker
              BUILDING_TYPES.HEADQUARTER, // 6
              BUILDING_TYPES.MARKET, // 1
  
              // Quest: Preparations
              BUILDING_TYPES.BARRACKS, // 4
              BUILDING_TYPES.WALL, // 2
              BUILDING_TYPES.WALL, // 3
          ],
          [
              BUILDING_TYPES.FARM, // 9
              BUILDING_TYPES.FARM, // 10
  
              BUILDING_TYPES.BARRACKS, // 5
              BUILDING_TYPES.WAREHOUSE, // 11
              BUILDING_TYPES.FARM, // 11
          ],
          [
              BUILDING_TYPES.BARRACKS, // 6
              BUILDING_TYPES.WAREHOUSE, // 12
              BUILDING_TYPES.FARM, // 12
  
              BUILDING_TYPES.BARRACKS, // 7
              BUILDING_TYPES.WAREHOUSE, // 13
              BUILDING_TYPES.FARM, // 13
          ],
          [
              BUILDING_TYPES.WALL, // 4
              BUILDING_TYPES.WALL, // 5
              BUILDING_TYPES.WALL, // 6
  
              BUILDING_TYPES.MARKET, // 2
              BUILDING_TYPES.MARKET, // 3
              BUILDING_TYPES.MARKET, // 4
          ],
          [
              BUILDING_TYPES.BARRACKS, // 8
              BUILDING_TYPES.BARRACKS, // 9
  
              BUILDING_TYPES.HEADQUARTER, // 7
              BUILDING_TYPES.HEADQUARTER, // 8
          ],
          [
              BUILDING_TYPES.TAVERN, // 1
              BUILDING_TYPES.TAVERN, // 2
              BUILDING_TYPES.TAVERN, // 3
  
              BUILDING_TYPES.RALLY_POINT, // 3
          ],
          [
              BUILDING_TYPES.BARRACKS, // 10
              BUILDING_TYPES.BARRACKS, // 11
  
              BUILDING_TYPES.WAREHOUSE, // 14
              BUILDING_TYPES.FARM, // 14
          ],
          [
              BUILDING_TYPES.WAREHOUSE, // 15
              BUILDING_TYPES.FARM, // 15
  
              BUILDING_TYPES.BARRACKS, // 12
              BUILDING_TYPES.BARRACKS, // 13
          ],
          [
              BUILDING_TYPES.STATUE, // 2
              BUILDING_TYPES.STATUE, // 3
  
              BUILDING_TYPES.WALL, // 7
              BUILDING_TYPES.WALL, // 8
          ],
          [
              BUILDING_TYPES.HEADQUARTER, // 9
              BUILDING_TYPES.HEADQUARTER, // 10
  
              BUILDING_TYPES.WAREHOUSE, // 16
              BUILDING_TYPES.FARM, // 16
              BUILDING_TYPES.FARM, // 17
          ],
          [
              BUILDING_TYPES.IRON_MINE, // 13
              BUILDING_TYPES.IRON_MINE, // 14
              BUILDING_TYPES.IRON_MINE, // 15
  
              BUILDING_TYPES.WAREHOUSE, // 17
          ],
          [
              BUILDING_TYPES.BARRACKS, // 14
              BUILDING_TYPES.BARRACKS, // 15
  
              BUILDING_TYPES.WAREHOUSE, // 18
              BUILDING_TYPES.FARM, // 18
          ],
          [
              BUILDING_TYPES.WALL, // 9
              BUILDING_TYPES.WALL, // 10
  
              BUILDING_TYPES.TAVERN, // 4
              BUILDING_TYPES.TAVERN, // 5
              BUILDING_TYPES.TAVERN, // 6
          ],
          [
              BUILDING_TYPES.MARKET, // 5
              BUILDING_TYPES.MARKET, // 6
              BUILDING_TYPES.MARKET, // 7
  
              BUILDING_TYPES.WAREHOUSE, // 19
              BUILDING_TYPES.FARM, // 19
              BUILDING_TYPES.WAREHOUSE, // 20
              BUILDING_TYPES.FARM, // 20
              BUILDING_TYPES.WAREHOUSE, // 21
              BUILDING_TYPES.FARM, // 21
          ],
          [
              BUILDING_TYPES.IRON_MINE, // 16
              BUILDING_TYPES.IRON_MINE, // 17
              BUILDING_TYPES.IRON_MINE, // 18
  
              BUILDING_TYPES.RALLY_POINT, // 4
          ],
          [
              BUILDING_TYPES.BARRACKS, // 16
              BUILDING_TYPES.BARRACKS, // 17
  
              BUILDING_TYPES.FARM, // 22
              BUILDING_TYPES.FARM, // 23
              BUILDING_TYPES.FARM, // 24
              BUILDING_TYPES.FARM, // 25
          ],
          [
              BUILDING_TYPES.WAREHOUSE, // 22
              BUILDING_TYPES.WAREHOUSE, // 23
  
              BUILDING_TYPES.HEADQUARTER, // 11
              BUILDING_TYPES.HEADQUARTER, // 12
          ],
          [
              BUILDING_TYPES.STATUE, // 4
              BUILDING_TYPES.STATUE, // 5
  
              BUILDING_TYPES.FARM, // 26
              BUILDING_TYPES.BARRACKS, // 18
          ],
          [
              BUILDING_TYPES.HEADQUARTER, // 14
              BUILDING_TYPES.HEADQUARTER, // 15
  
              BUILDING_TYPES.FARM, // 27
              BUILDING_TYPES.BARRACKS, // 19
          ],
          [
              BUILDING_TYPES.HEADQUARTER, // 15
              BUILDING_TYPES.HEADQUARTER, // 16
  
              BUILDING_TYPES.BARRACKS, // 20
  
              BUILDING_TYPES.HEADQUARTER, // 17
              BUILDING_TYPES.HEADQUARTER, // 18
              BUILDING_TYPES.HEADQUARTER, // 19
              BUILDING_TYPES.HEADQUARTER, // 20
          ],
          [
              BUILDING_TYPES.ACADEMY, // 1
  
              BUILDING_TYPES.FARM, // 28
              BUILDING_TYPES.WAREHOUSE, // 23
              BUILDING_TYPES.WAREHOUSE, // 24
              BUILDING_TYPES.WAREHOUSE, // 25
          ],
          [
              BUILDING_TYPES.MARKET, // 8
              BUILDING_TYPES.MARKET, // 9
              BUILDING_TYPES.MARKET, // 10
  
              BUILDING_TYPES.TIMBER_CAMP, // 13
              BUILDING_TYPES.CLAY_PIT, // 13
              BUILDING_TYPES.IRON_MINE, // 19
          ],
          [
              BUILDING_TYPES.TIMBER_CAMP, // 14
              BUILDING_TYPES.CLAY_PIT, // 14
              BUILDING_TYPES.TIMBER_CAMP, // 15
              BUILDING_TYPES.CLAY_PIT, // 15
  
              BUILDING_TYPES.TIMBER_CAMP, // 16
              BUILDING_TYPES.TIMBER_CAMP, // 17
          ],
          [
              BUILDING_TYPES.WALL, // 11
              BUILDING_TYPES.WALL, // 12
  
              BUILDING_TYPES.MARKET, // 11
              BUILDING_TYPES.MARKET, // 12
              BUILDING_TYPES.MARKET, // 13
          ],
          [
              BUILDING_TYPES.TIMBER_CAMP, // 18
              BUILDING_TYPES.CLAY_PIT, // 16
              BUILDING_TYPES.TIMBER_CAMP, // 19
              BUILDING_TYPES.CLAY_PIT, // 17
  
              BUILDING_TYPES.TAVERN, // 7
              BUILDING_TYPES.TAVERN, // 8
              BUILDING_TYPES.TAVERN, // 9
          ],
          [
              BUILDING_TYPES.WALL, // 13
              BUILDING_TYPES.WALL, // 14
  
              BUILDING_TYPES.TIMBER_CAMP, // 20
              BUILDING_TYPES.CLAY_PIT, // 18
              BUILDING_TYPES.IRON_MINE, // 20
          ],
          [
              BUILDING_TYPES.TIMBER_CAMP, // 21
              BUILDING_TYPES.CLAY_PIT, // 19
              BUILDING_TYPES.IRON_MINE, // 21
  
              BUILDING_TYPES.BARRACKS, // 21
              BUILDING_TYPES.BARRACKS, // 22
              BUILDING_TYPES.BARRACKS, // 23
          ],
          [
              BUILDING_TYPES.FARM, // 29
              BUILDING_TYPES.WAREHOUSE, // 26
              BUILDING_TYPES.WAREHOUSE, // 27
  
              BUILDING_TYPES.TAVERN, // 10
              BUILDING_TYPES.TAVERN, // 11
              BUILDING_TYPES.TAVERN, // 12
          ],
          [
              BUILDING_TYPES.TIMBER_CAMP, // 22
              BUILDING_TYPES.CLAY_PIT, // 20
              BUILDING_TYPES.IRON_MINE, // 22
  
              BUILDING_TYPES.TIMBER_CAMP, // 23
              BUILDING_TYPES.CLAY_PIT, // 21
              BUILDING_TYPES.IRON_MINE, // 23
          ],
          [
              BUILDING_TYPES.TIMBER_CAMP, // 24
              BUILDING_TYPES.CLAY_PIT, // 22
              BUILDING_TYPES.IRON_MINE, // 24
  
              BUILDING_TYPES.BARRACKS, // 24
              BUILDING_TYPES.BARRACKS, // 25
          ],
          [
              BUILDING_TYPES.FARM, // 30
              BUILDING_TYPES.WAREHOUSE, // 28
              BUILDING_TYPES.WAREHOUSE, // 29
  
              BUILDING_TYPES.WALL, // 15
              BUILDING_TYPES.WALL, // 16
              BUILDING_TYPES.WALL, // 17
              BUILDING_TYPES.WALL, // 18
          ],
          [
              BUILDING_TYPES.TAVERN, // 13
              BUILDING_TYPES.TAVERN, // 14
  
              BUILDING_TYPES.RALLY_POINT, // 5
  
              BUILDING_TYPES.TIMBER_CAMP, // 25
              BUILDING_TYPES.CLAY_PIT, // 23
              BUILDING_TYPES.IRON_MINE, // 25
          ],
          [
              BUILDING_TYPES.TIMBER_CAMP, // 26
              BUILDING_TYPES.CLAY_PIT, // 24
              BUILDING_TYPES.IRON_MINE, // 26
  
              BUILDING_TYPES.TIMBER_CAMP, // 27
              BUILDING_TYPES.CLAY_PIT, // 25
              BUILDING_TYPES.IRON_MINE, // 27
          ],
          [
              BUILDING_TYPES.TIMBER_CAMP, // 28
              BUILDING_TYPES.CLAY_PIT, // 26
              BUILDING_TYPES.IRON_MINE, // 28
  
              BUILDING_TYPES.TIMBER_CAMP, // 29
              BUILDING_TYPES.CLAY_PIT, // 27
              BUILDING_TYPES.CLAY_PIT, // 28
              BUILDING_TYPES.IRON_MINE, // 29
          ],
          [
              BUILDING_TYPES.TIMBER_CAMP, // 30
              BUILDING_TYPES.CLAY_PIT, // 29
              BUILDING_TYPES.CLAY_PIT, // 30
              BUILDING_TYPES.IRON_MINE, // 30
  
              BUILDING_TYPES.WALL, // 19
              BUILDING_TYPES.WALL, // 20
          ]
      ]
  
      defaultSequences['Full Village'] = [
          [
              BUILDING_TYPES.HOSPITAL, // 2
              BUILDING_TYPES.HOSPITAL, // 3
              BUILDING_TYPES.HOSPITAL, // 4
              BUILDING_TYPES.HOSPITAL, // 5
  
              BUILDING_TYPES.MARKET, // 14
              BUILDING_TYPES.MARKET, // 15
              BUILDING_TYPES.MARKET, // 16
              BUILDING_TYPES.MARKET, // 17
          ],
          [
              BUILDING_TYPES.HEADQUARTER, // 21
              BUILDING_TYPES.HEADQUARTER, // 22
              BUILDING_TYPES.HEADQUARTER, // 23
              BUILDING_TYPES.HEADQUARTER, // 24
              BUILDING_TYPES.HEADQUARTER, // 25
  
              BUILDING_TYPES.PRECEPTORY, // 1
  
              BUILDING_TYPES.HOSPITAL, // 6
              BUILDING_TYPES.HOSPITAL, // 7
              BUILDING_TYPES.HOSPITAL, // 8
              BUILDING_TYPES.HOSPITAL, // 9
              BUILDING_TYPES.HOSPITAL, // 10
          ],
          [
              BUILDING_TYPES.MARKET, // 18
              BUILDING_TYPES.MARKET, // 19
              BUILDING_TYPES.MARKET, // 20
              BUILDING_TYPES.MARKET, // 21
  
              BUILDING_TYPES.PRECEPTORY, // 2
              BUILDING_TYPES.PRECEPTORY, // 3
  
              BUILDING_TYPES.MARKET, // 22
              BUILDING_TYPES.MARKET, // 23
              BUILDING_TYPES.MARKET, // 24
              BUILDING_TYPES.MARKET, // 25
          ],
          [
              BUILDING_TYPES.HEADQUARTER, // 26
              BUILDING_TYPES.HEADQUARTER, // 27
              BUILDING_TYPES.HEADQUARTER, // 28
              BUILDING_TYPES.HEADQUARTER, // 29
              BUILDING_TYPES.HEADQUARTER, // 30
  
              BUILDING_TYPES.PRECEPTORY, // 4
              BUILDING_TYPES.PRECEPTORY, // 5
              BUILDING_TYPES.PRECEPTORY, // 6
              BUILDING_TYPES.PRECEPTORY, // 7
              BUILDING_TYPES.PRECEPTORY, // 8
              BUILDING_TYPES.PRECEPTORY, // 9
              BUILDING_TYPES.PRECEPTORY, // 10
          ]
      ]
  
      Array.prototype.unshift.apply(
          defaultSequences['Full Village'],
          defaultSequences['Essential']
      )
  
      defaultSequences['Essential Without Wall'] =
          defaultSequences['Essential'].filter(function (building) {
              return building !== BUILDING_TYPES.WALL
          })
  
      defaultSequences['Full Wall'] = [
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL,
          BUILDING_TYPES.WALL // 20
      ]
  
      return parseSequences(defaultSequences)
  })
  
  define('two/builderQueue/errorCodes', [], function () {
      return {
          SEQUENCE_NO_EXISTS: 'sequence_no_exists',
          SEQUENCE_EXISTS: 'sequence_exists',
          SEQUENCE_INVALID: 'sequence_invalid'
      }
  })
  
  define('two/builderQueue/events', [], function () {
      angular.extend(eventTypeProvider, {
          BUILDER_QUEUE_JOB_STARTED: 'builder_queue_job_started',
          BUILDER_QUEUE_START: 'builder_queue_start',
          BUILDER_QUEUE_STOP: 'builder_queue_stop',
          BUILDER_QUEUE_UNKNOWN_SETTING: 'builder_queue_settings_unknown_setting',
          BUILDER_QUEUE_CLEAR_LOGS: 'builder_queue_clear_logs',
          BUILDER_QUEUE_BUILDING_SEQUENCES_UPDATED: 'builder_queue_building_orders_updated',
          BUILDER_QUEUE_BUILDING_SEQUENCES_ADDED: 'builder_queue_building_orders_added',
          BUILDER_QUEUE_BUILDING_SEQUENCES_REMOVED: 'builder_queue_building_orders_removed',
          BUILDER_QUEUE_SETTINGS_CHANGE: 'builder_queue_settings_change',
          BUILDER_QUEUE_NO_SEQUENCES: 'builder_queue_no_sequences'
      })
  })
  
  
  define('two/builderQueue/ui', [
      'two/ui',
      'two/ui/button',
      'two/builderQueue',
      'two/utils',
      'two/ready',
      'two/Settings',
      'two/builderQueue/settings',
      'two/builderQueue/settingsMap',
      'two/builderQueue/errorCodes',
      'conf/buildingTypes',
      'two/EventScope',
      'queues/EventQueue',
      'helper/time'
  ], function (
      interfaceOverflow,
      FrontButton,
      builderQueue,
      utils,
      ready,
      Settings,
      SETTINGS,
      SETTINGS_MAP,
      ERROR_CODES,
      BUILDING_TYPES,
      EventScope,
      eventQueue,
      timeHelper
  ) {
      var eventScope
      var $scope
      var textObject = 'builder_queue'
      var textObjectCommon = 'common'
      var groupList = modelDataService.getGroupList()
      var groups = []
      var buildingsLevelPoints = {}
      var running = false
      var gameDataBuildings
      var editorView = {
          sequencesAvail: true,
          modal: {}
      }
      var settings
      var settingsView = {
          sequencesAvail: true
      }
      var logsView = {}
      var TAB_TYPES = {
          SETTINGS: 'settings',
          SEQUENCES: 'sequences',
          LOGS: 'logs'
      }
  
      var buildingLevelReached = function (building, level) {
          var buildingData = modelDataService.getSelectedVillage().getBuildingData()
          return buildingData.getBuildingLevel(building) >= level
      }
  
      var buildingLevelProgress = function (building, level) {
          var queue = modelDataService.getSelectedVillage().getBuildingQueue().getQueue()
          var progress = false
  
          queue.some(function (job) {
              if (job.building === building && job.level === level) {
                  return progress = true
              }
          })
  
          return progress
      }
  
      /**
       * Calculate the total of points accumulated ultil the specified level.
       */
      var getLevelScale = function (factor, base, level) {
          return level ? parseInt(Math.round(factor * Math.pow(base, level - 1)), 10) : 0
      }
  
      var moveArrayItem = function (obj, oldIndex, newIndex) {
          if (newIndex >= obj.length) {
              var i = newIndex - obj.length + 1
              
              while (i--) {
                  obj.push(undefined)
              }
          }
  
          obj.splice(newIndex, 0, obj.splice(oldIndex, 1)[0])
      }
  
      var parseBuildingSequence = function (sequence) {
          return sequence.map(function (item) {
              return item.building
          })
      }
  
      var createBuildingSequence = function (sequenceId, sequence) {
          var error = builderQueue.addBuildingSequence(sequenceId, sequence)
  
          switch (error) {
          case ERROR_CODES.SEQUENCE_EXISTS:
              utils.emitNotif('error', $filter('i18n')('error_sequence_exists', $rootScope.loc.ale, textObject))
              return false
  
              break
          case ERROR_CODES.SEQUENCE_INVALID:
              utils.emitNotif('error', $filter('i18n')('error_sequence_invalid', $rootScope.loc.ale, textObject))
              return false
  
              break
          }
  
          return true
      }
  
      var selectSome = function (obj) {
          for (var i in obj) {
              if (obj.hasOwnProperty(i)) {
                  return i
              }
          }
  
          return false
      }
  
      settingsView.generateSequences = function () {
          var sequences = settings.getSetting(SETTINGS.BUILDING_SEQUENCES)
          var sequencesAvail = Object.keys(sequences).length
  
          settingsView.sequencesAvail = sequencesAvail
  
          if (!sequencesAvail) {
              return false
          }
  
          settingsView.generateBuildingSequence()
          settingsView.generateBuildingSequenceFinal()
          settingsView.updateVisibleBuildingSequence()
      }
  
      settingsView.generateBuildingSequence = function () {
          var sequenceId = $scope.settings[SETTINGS.ACTIVE_SEQUENCE].value
          var buildingSequenceRaw = $scope.settings[SETTINGS.BUILDING_SEQUENCES][sequenceId]
          var buildingData = modelDataService.getGameData().getBuildings()
          var buildingLevels = {}
          var building
          var level
          var state
          var price
  
          settingsView.sequencesAvail = !!buildingSequenceRaw
  
          if (!settingsView.sequencesAvail) {
              return false
          }
  
          for (building in BUILDING_TYPES) {
              buildingLevels[BUILDING_TYPES[building]] = 0
          }
  
          settingsView.buildingSequence = buildingSequenceRaw.map(function (building) {
              level = ++buildingLevels[building]
              price = buildingData[building].individual_level_costs[level]
              state = 'not-reached'
  
              if (buildingLevelReached(building, level)) {
                  state = 'reached'
              } else if (buildingLevelProgress(building, level)) {
                  state = 'progress'
              }
  
              return {
                  level: level,
                  price: buildingData[building].individual_level_costs[level],
                  building: building,
                  duration: timeHelper.readableSeconds(price.build_time),
                  levelPoints: buildingsLevelPoints[building][level - 1],
                  state: state
              }
          })
      }
  
      settingsView.generateBuildingSequenceFinal = function (_sequenceId) {
          var selectedSequence = $scope.settings[SETTINGS.ACTIVE_SEQUENCE].value
          var sequenceBuildings = $scope.settings[SETTINGS.BUILDING_SEQUENCES][_sequenceId || selectedSequence]
          var sequenceObj = {}
          var sequence = []
          var building
  
          for (building in gameDataBuildings) {
              sequenceObj[building] = {
                  level: 0,
                  order: gameDataBuildings[building].order
              }
          }
  
          sequenceBuildings.forEach(function (building) {
              sequenceObj[building].level++
          })
  
          for (building in sequenceObj) {
              if (sequenceObj[building].level !== 0) {
                  sequence.push({
                      building: building,
                      level: sequenceObj[building].level,
                      order: sequenceObj[building].order
                  })
              }
          }
  
          settingsView.buildingSequenceFinal = sequence
      }
  
      settingsView.updateVisibleBuildingSequence = function () {
          var offset = $scope.pagination.buildingSequence.offset
          var limit = $scope.pagination.buildingSequence.limit
  
          settingsView.visibleBuildingSequence = settingsView.buildingSequence.slice(offset, offset + limit)
          $scope.pagination.buildingSequence.count = settingsView.buildingSequence.length
      }
  
      settingsView.generateBuildingsLevelPoints = function () {
          var $gameData = modelDataService.getGameData()
          var buildingName
          var buildingData
          var buildingTotalPoints
          var levelPoints
          var currentLevelPoints
          var level
  
          for(buildingName in $gameData.data.buildings) {
              buildingData = $gameData.getBuildingDataForBuilding(buildingName)
              buildingTotalPoints = 0
              buildingsLevelPoints[buildingName] = []
  
              for (level = 1; level <= buildingData.max_level; level++) {
                  currentLevelPoints  = getLevelScale(buildingData.points, buildingData.points_factor, level)
                  levelPoints = currentLevelPoints - buildingTotalPoints
                  buildingTotalPoints += levelPoints
                  buildingsLevelPoints[buildingName].push(levelPoints)
              }
          }
      }
  
      editorView.moveUp = function () {
          var copy = angular.copy(editorView.buildingSequence)
          var index
          var item
  
          for (index = 0; index < copy.length; index++) {
              item = copy[index]
  
              if (!item.checked) {
                  continue
              }
  
              if (index === 0) {
                  continue
              }
  
              if (copy[index - 1].checked) {
                  continue
              }
  
              if (copy[index - 1].building === item.building) {
                  copy[index - 1].level++
                  item.level--
              }
  
              moveArrayItem(copy, index, index - 1)
          }
  
          editorView.buildingSequence = copy
          editorView.updateVisibleBuildingSequence()
      }
  
      editorView.moveDown = function () {
          var copy = angular.copy(editorView.buildingSequence)
          var index
          var item
  
          for (index = copy.length - 1; index >= 0; index--) {
              item = copy[index]
  
              if (!item.checked) {
                  continue
              }
  
              if (index === copy.length - 1) {
                  continue
              }
  
              if (copy[index + 1].checked) {
                  continue
              }
  
              if (copy[index + 1].building === item.building) {
                  copy[index + 1].level--
                  item.level++
              }
  
              moveArrayItem(copy, index, index + 1)
          }
  
          editorView.buildingSequence = copy
          editorView.updateVisibleBuildingSequence()
      }
  
      editorView.addBuilding = function (building, position) {
          var index = position - 1
          var newSequence = editorView.buildingSequence.slice()
          var updated
  
          newSequence.splice(index, 0, {
              level: null,
              building: building,
              checked: false
          })
  
          newSequence = editorView.updateLevels(newSequence, building)
  
          if (!newSequence) {
              return false
          }
  
          editorView.buildingSequence = newSequence
          editorView.updateVisibleBuildingSequence()
  
          return true
      }
  
      editorView.removeBuilding = function (index) {
          var building = editorView.buildingSequence[index].building
  
          editorView.buildingSequence.splice(index, 1)
          editorView.buildingSequence = editorView.updateLevels(editorView.buildingSequence, building)
  
          editorView.updateVisibleBuildingSequence()
      }
  
      editorView.updateLevels = function (sequence, building) {
          var buildingLevel = 0
          var modifiedSequence = []
          var i
          var item
          var limitExceeded = false
  
          for (i = 0; i < sequence.length; i++) {
              item = sequence[i]
  
              if (item.building === building) {
                  buildingLevel++
  
                  if (buildingLevel > gameDataBuildings[building].max_level) {
                      limitExceeded = true
                      break
                  }
  
                  modifiedSequence.push({
                      level: buildingLevel,
                      building: building,
                      checked: false
                  })
              } else {
                  modifiedSequence.push(item)
              }
          }
  
          if (limitExceeded) {
              return false
          }
  
          return modifiedSequence
      }
  
      editorView.generateBuildingSequence = function () {
          var sequences = settings.getSetting(SETTINGS.BUILDING_SEQUENCES)
          var sequencesAvail = Object.keys(sequences).length
  
          editorView.sequencesAvail = sequencesAvail
  
          if (!sequencesAvail) {
              return false
          }
  
          var sequenceId = editorView.selectedSequence.value
          var buildingSequenceRaw = sequences[sequenceId]
          var buildingLevels = {}
          var building
  
          for (building in BUILDING_TYPES) {
              buildingLevels[BUILDING_TYPES[building]] = 0
          }
  
          editorView.buildingSequence = buildingSequenceRaw.map(function (building) {
              return {
                  level: ++buildingLevels[building],
                  building: building,
                  checked: false
              }
          })
  
          editorView.updateVisibleBuildingSequence()
      }
  
      editorView.updateVisibleBuildingSequence = function () {
          var offset = $scope.pagination.buildingSequenceEditor.offset
          var limit = $scope.pagination.buildingSequenceEditor.limit
  
          editorView.visibleBuildingSequence = editorView.buildingSequence.slice(offset, offset + limit)
          $scope.pagination.buildingSequenceEditor.count = editorView.buildingSequence.length
      }
  
      editorView.updateBuildingSequence = function () {
          var selectedSequence = editorView.selectedSequence.value
          var parsedSequence = parseBuildingSequence(editorView.buildingSequence)
          var error = builderQueue.updateBuildingSequence(selectedSequence, parsedSequence)
  
          switch (error) {
          case ERROR_CODES.SEQUENCE_NO_EXISTS:
              utils.emitNotif('error', $filter('i18n')('error_sequence_no_exits', $rootScope.loc.ale, textObject))
  
              break
          case ERROR_CODES.SEQUENCE_INVALID:
              utils.emitNotif('error', $filter('i18n')('error_sequence_invalid', $rootScope.loc.ale, textObject))
  
              break
          }
      }
  
      editorView.modal.removeSequence = function () {
          var modalScope = $rootScope.$new()
          var textObject = 'builder_queue_remove_sequence_modal'
  
          modalScope.title = $filter('i18n')('title', $rootScope.loc.ale, textObject)
          modalScope.text = $filter('i18n')('text', $rootScope.loc.ale, textObject)
          modalScope.submitText = $filter('i18n')('remove', $rootScope.loc.ale, textObjectCommon)
          modalScope.cancelText = $filter('i18n')('cancel', $rootScope.loc.ale, textObjectCommon)
  
          modalScope.submit = function () {
              modalScope.closeWindow()
              builderQueue.removeSequence(editorView.selectedSequence.value)
          }
  
          modalScope.cancel = function () {
              modalScope.closeWindow()
          }
  
          windowManagerService.getModal('modal_attention', modalScope)
      }
  
      editorView.modal.addBuilding = function () {
          var modalScope = $rootScope.$new()
          var building
  
          modalScope.buildings = []
          modalScope.position = 1
          modalScope.indexLimit = editorView.buildingSequence.length + 1
          modalScope.selectedBuilding = {
              name: $filter('i18n')(BUILDING_TYPES.HEADQUARTER, $rootScope.loc.ale, 'building_names'),
              value: BUILDING_TYPES.HEADQUARTER
          }
  
          for (building in gameDataBuildings) {
              modalScope.buildings.push({
                  name: $filter('i18n')(building, $rootScope.loc.ale, 'building_names'),
                  value: building
              })
          }
  
          modalScope.add = function () {
              var building = modalScope.selectedBuilding.value
              var position = modalScope.position
              var buildingName = $filter('i18n')(building, $rootScope.loc.ale, 'building_names')
              var buildingLimit = gameDataBuildings[building].max_level
  
              if (editorView.addBuilding(building, position)) {
                  modalScope.closeWindow()
                  utils.emitNotif('success', $filter('i18n')('add_building_success', $rootScope.loc.ale, textObject, buildingName, position))
              } else {
                  utils.emitNotif('error', $filter('i18n')('add_building_limit_exceeded', $rootScope.loc.ale, textObject, buildingName, buildingLimit))
              }
          }
  
          windowManagerService.getModal('!twoverflow_builder_queue_add_building_modal', modalScope)
      }
  
      editorView.modal.nameSequence = function () {
          var modalScope = $rootScope.$new()
          var selectedSequenceName = editorView.selectedSequence.name
          var selectedSequence = $scope.settings[SETTINGS.BUILDING_SEQUENCES][selectedSequenceName]
          
          modalScope.name = selectedSequenceName
  
          modalScope.submit = function () {
              if (modalScope.name.length < 3) {
                  utils.emitNotif('error', $filter('i18n')('name_sequence_min_lenght', $rootScope.loc.ale, textObject))
                  return false
              }
  
              if (createBuildingSequence(modalScope.name, selectedSequence)) {
                  modalScope.closeWindow()
              }
          }
  
          windowManagerService.getModal('!twoverflow_builder_queue_name_sequence_modal', modalScope)
      }
  
      logsView.updateVisibleLogs = function () {
          var offset = $scope.pagination.logs.offset
          var limit = $scope.pagination.logs.limit
  
          logsView.visibleLogs = logsView.logs.slice(offset, offset + limit)
          $scope.pagination.logs.count = logsView.logs.length
      }
  
      logsView.clearLogs = function () {
          builderQueue.clearLogs()
      }
  
      var createFirstSequence = function () {
          var modalScope = $rootScope.$new()
          var initialSequence = [BUILDING_TYPES.HEADQUARTER]
  
          modalScope.name = ''
          
          modalScope.submit = function () {
              if (modalScope.name.length < 3) {
                  utils.emitNotif('error', $filter('i18n')('name_sequence_min_lenght', $rootScope.loc.ale, textObject))
                  return false
              }
  
              if (createBuildingSequence(modalScope.name, initialSequence)) {
                  $scope.settings[SETTINGS.ACTIVE_SEQUENCE] = { name: modalScope.name, value: modalScope.name }
                  $scope.settings[SETTINGS.BUILDING_SEQUENCES][modalScope.name] = initialSequence
  
                  saveSettings()
  
                  settingsView.selectedSequence = { name: modalScope.name, value: modalScope.name }
                  editorView.selectedSequence = { name: modalScope.name, value: modalScope.name }
  
                  settingsView.generateSequences()
                  editorView.generateBuildingSequence()
  
                  modalScope.closeWindow()
                  selectTab(TAB_TYPES.SEQUENCES)
              }
          }
  
          windowManagerService.getModal('!twoverflow_builder_queue_name_sequence_modal', modalScope)
      }
  
      var selectTab = function (tabType) {
          $scope.selectedTab = tabType
      }
  
      var saveSettings = function () {
          settings.setSettings(settings.decodeSettings($scope.settings))
      }
  
      var switchBuilder = function () {
          if (builderQueue.isRunning()) {
              builderQueue.stop()
          } else {
              builderQueue.start()
          }
      }
  
      var eventHandlers = {
          updateGroups: function () {
              $scope.groups = Settings.encodeList(groupList.getGroups(), {
                  type: 'groups',
                  disabled: true
              })
          },
          updateSequences: function () {
              var sequences = settings.getSetting(SETTINGS.BUILDING_SEQUENCES)
              
              $scope.sequences = Settings.encodeList(sequences, {
                  type: 'keys',
                  disabled: false
              })
          },
          generateBuildingSequences: function () {
              settingsView.generateSequences()
          },
          generateBuildingSequencesEditor: function () {
              editorView.generateBuildingSequence()
          },
          updateLogs: function () {
              $scope.logs = builderQueue.getLogs()
              logsView.updateVisibleLogs()
          },
          clearLogs: function () {
              utils.emitNotif('success', $filter('i18n')('logs_cleared', $rootScope.loc.ale, textObject))
              eventHandlers.updateLogs()
          },
          buildingSequenceUpdate: function (event, sequenceId) {
              var sequences = settings.getSetting(SETTINGS.BUILDING_SEQUENCES)
              $scope.settings[SETTINGS.BUILDING_SEQUENCES][sequenceId] = sequences[sequenceId]
  
              if ($scope.settings[SETTINGS.ACTIVE_SEQUENCE].value === sequenceId) {
                  settingsView.generateSequences()
              }
  
              utils.emitNotif('success', $filter('i18n')('sequence_updated', $rootScope.loc.ale, textObject, sequenceId))
          },
          buildingSequenceAdd: function (event, sequenceId) {
              var sequences = settings.getSetting(SETTINGS.BUILDING_SEQUENCES)
              $scope.settings[SETTINGS.BUILDING_SEQUENCES][sequenceId] = sequences[sequenceId]
              eventHandlers.updateSequences()
              utils.emitNotif('success', $filter('i18n')('sequence_created', $rootScope.loc.ale, textObject, sequenceId))
          },
          buildingSequenceRemoved: function (event, sequenceId) {
              var substituteSequence
  
              delete $scope.settings[SETTINGS.BUILDING_SEQUENCES][sequenceId]
  
              substituteSequence = selectSome($scope.settings[SETTINGS.BUILDING_SEQUENCES])
              editorView.selectedSequence = { name: substituteSequence, value: substituteSequence }
              eventHandlers.updateSequences()
              editorView.generateBuildingSequence()
  
              if (settings.getSetting(SETTINGS.ACTIVE_SEQUENCE) === sequenceId) {
                  settings.setSetting(SETTINGS.ACTIVE_SEQUENCE, substituteSequence, {
                      quiet: true
                  })
                  settingsView.generateSequences()
              }
  
              utils.emitNotif('success', $filter('i18n')('sequence_removed', $rootScope.loc.ale, textObject, sequenceId))
          },
          saveSettings: function () {
              utils.emitNotif('success', $filter('i18n')('settings_saved', $rootScope.loc.ale, textObject))
          },
          started: function () {
              $scope.running = true
          },
          stopped: function () {
              $scope.running = false
          }
      }
  
      var init = function () {
          gameDataBuildings = modelDataService.getGameData().getBuildings()
          settingsView.generateBuildingsLevelPoints()
          settings = builderQueue.getSettings()
  
          var opener = new FrontButton('Builder', {
              classHover: false,
              classBlur: false,
              onClick: buildWindow
          })
  
          eventQueue.register(eventTypeProvider.BUILDER_QUEUE_START, function () {
              running = true
              opener.$elem.classList.remove('btn-green')
              opener.$elem.classList.add('btn-red')
              utils.emitNotif('success', $filter('i18n')('started', $rootScope.loc.ale, textObject))
          })
  
          eventQueue.register(eventTypeProvider.BUILDER_QUEUE_STOP, function () {
              running = false
              opener.$elem.classList.remove('btn-red')
              opener.$elem.classList.add('btn-green')
              utils.emitNotif('success', $filter('i18n')('stopped', $rootScope.loc.ale, textObject))
          })
  
          interfaceOverflow.addTemplate('twoverflow_builder_queue_window', `<div id="two-builder-queue" class="win-content two-window"><header class="win-head"><h2>BuilderQueue</h2><ul class="list-btn"><li><a href="#" class="size-34x34 btn-red icon-26x26-close" ng-click="closeWindow()"></a></li></ul></header><div class="win-main small-select" scrollbar=""><div class="tabs tabs-bg"><div class="tabs-three-col"><div class="tab" ng-click="selectTab(TAB_TYPES.SETTINGS)" ng-class="{'tab-active': selectedTab == TAB_TYPES.SETTINGS}"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.SETTINGS}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.SETTINGS}">{{ TAB_TYPES.SETTINGS | i18n:loc.ale:textObjectCommon }}</a></div></div></div><div class="tab" ng-click="selectTab(TAB_TYPES.SEQUENCES)" ng-class="{'tab-active': selectedTab == TAB_TYPES.SEQUENCES}"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.SEQUENCES}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.SEQUENCES}">{{ TAB_TYPES.SEQUENCES | i18n:loc.ale:textObject }}</a></div></div></div><div class="tab" ng-click="selectTab(TAB_TYPES.LOGS)" ng-class="{'tab-active': selectedTab == TAB_TYPES.LOGS}"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.LOGS}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.LOGS}">{{ TAB_TYPES.LOGS | i18n:loc.ale:textObjectCommon }}</a></div></div></div></div></div><div class="box-paper footer"><div class="scroll-wrap"><div ng-show="selectedTab === TAB_TYPES.SETTINGS"><h5 class="twx-section">{{ 'settings' | i18n:loc.ale:textObject }}</h5><table class="tbl-border-light tbl-striped"><colgroup><col width="50%"><col></colgroup><tbody><tr><td><span class="ff-cell-fix">{{ 'settings_village_groups' | i18n:loc.ale:textObject }}</span></td><td><div select="" list="groups" selected="settings[SETTINGS.GROUP_VILLAGES]" drop-down="true"></div></td></tr><tr ng-show="settingsView.sequencesAvail"><td><span class="ff-cell-fix">{{ 'settings_building_sequence' | i18n:loc.ale:textObject }}</span></td><td><div select="" list="sequences" selected="settings[SETTINGS.ACTIVE_SEQUENCE]" drop-down="true"></div></td></tr></tbody></table><h5 class="twx-section">{{ 'settings_building_sequence' | i18n:loc.ale:textObject }}</h5><p ng-show="!settingsView.sequencesAvail" class="text-center"><a href="#" class="btn-orange btn-border create-sequence" ng-click="createFirstSequence()">{{ 'create_sequence' | i18n:loc.ale:textObject }}</a></p><div ng-if="settingsView.sequencesAvail"><div class="page-wrap" pagination="pagination.buildingSequence"></div><table class="tbl-border-light header-center building-sequence"><colgroup><col width="5%"><col width="30%"><col width="7%"><col width="13%"><col width="8%"><col></colgroup><tr><th tooltip="" tooltip-content="{{ 'position' | i18n:loc.ale:textObject }}">#</th><th>{{ 'building' | i18n:loc.ale:textObjectCommon }}</th><th>{{ 'level' | i18n:loc.ale:textObjectCommon }}</th><th>{{ 'duration' | i18n:loc.ale:textObjectCommon }}</th><th>{{ 'points' | i18n:loc.ale:textObjectCommon }}</th><th>{{ 'costs' | i18n:loc.ale:textObjectCommon }}</th></tr><tr ng-repeat="item in settingsView.visibleBuildingSequence track by $index" class="{{ item.state }}"><td>{{ pagination.buildingSequence.offset + $index + 1 }}</td><td><span class="building-icon icon-20x20-building-{{ item.building }}"></span> {{ item.building | i18n:loc.ale:'building_names' }}</td><td>{{ item.level }}</td><td>{{ item.duration }}</td><td class="green">+{{ item.levelPoints }}</td><td><span class="icon-26x26-resource-wood"></span> {{ item.price.wood }} <span class="icon-26x26-resource-clay"></span> {{ item.price.clay }} <span class="icon-26x26-resource-iron"></span> {{ item.price.iron }}</td></tr></table><div class="page-wrap" pagination="pagination.buildingSequence"></div></div><h5 ng-if="settingsView.sequencesAvail" class="twx-section">{{ 'settings_building_sequence_final' | i18n:loc.ale:textObject }}</h5><table ng-if="settingsView.sequencesAvail" class="tbl-border-light tbl-striped header-center building-sequence-final"><colgroup><col width="5%"><col width="70%"><col></colgroup><tr><th colspan="2">{{ 'building' | i18n:loc.ale:textObjectCommon }}</th><th>{{ 'level' | i18n:loc.ale:textObjectCommon }}</th></tr><tr ng-repeat="item in settingsView.buildingSequenceFinal | orderBy:'order'"><td><span class="building-icon icon-20x20-building-{{ item.building }}"></span></td><td>{{ item.building | i18n:loc.ale:'building_names' }}</td><td>{{ item.level }}</td></tr></table></div><div ng-show="selectedTab === TAB_TYPES.SEQUENCES"><h5 class="twx-section">{{ 'sequences_edit_sequence' | i18n:loc.ale:textObject }}</h5><p ng-show="!editorView.sequencesAvail" class="text-center"><a href="#" class="btn-orange btn-border create-sequence" ng-click="createFirstSequence()">{{ 'create_sequence' | i18n:loc.ale:textObject }}</a></p><table ng-if="editorView.sequencesAvail" class="tbl-border-light tbl-striped editor-select-sequence"><colgroup><col width="50%"><col></colgroup><tbody><tr><td><span class="ff-cell-fix">{{ 'sequences_select_edit' | i18n:loc.ale:textObject }}</span></td><td><div class="select-sequence-editor" select="" list="sequences" selected="editorView.selectedSequence" drop-down="true"></div><a class="btn btn-orange clone-sequence" ng-click="editorView.modal.nameSequence()" tooltip="" tooltip-content="{{ 'tooltip_clone' | i18n:loc.ale:textObject }}">{{ 'clone' | i18n:loc.ale:textObject }}</a> <a href="#" class="btn-red remove-sequence icon-20x20-close" ng-click="editorView.modal.removeSequence()" tooltip="" tooltip-content="{{ 'tooltip_remove_sequence' | i18n:loc.ale:textObject }}"></a></td></tr></tbody></table><div ng-if="editorView.sequencesAvail"><div class="page-wrap" pagination="pagination.buildingSequenceEditor"></div><table class="tbl-border-light tbl-striped header-center building-sequence-editor"><colgroup><col width="5%"><col width="5%"><col><col width="7%"><col width="10%"></colgroup><tr><th></th><th tooltip="" tooltip-content="{{ 'position' | i18n:loc.ale:textObject }}">#</th><th>{{ 'building' | i18n:loc.ale:textObjectCommon }}</th><th>{{ 'level' | i18n:loc.ale:textObjectCommon }}</th><th>{{ 'actions' | i18n:loc.ale:textObjectCommon }}</th></tr><tr ng-repeat="item in editorView.visibleBuildingSequence track by $index" ng-class="{'selected': item.checked}"><td><label class="size-26x26 btn-orange icon-26x26-checkbox" ng-class="{'icon-26x26-checkbox-checked': item.checked}"><input type="checkbox" ng-model="item.checked"></label></td><td>{{ pagination.buildingSequenceEditor.offset + $index + 1 }}</td><td><span class="building-icon icon-20x20-building-{{ item.building }}"></span> {{ item.building | i18n:loc.ale:'building_names' }}</td><td>{{ item.level }}</td><td><a href="#" class="size-20x20 btn-red icon-20x20-close" ng-click="editorView.removeBuilding(pagination.buildingSequenceEditor.offset + $index)" tooltip="" tooltip-content="{{ 'remove_building' | i18n:loc.ale:textObject }}"></a></td></tr></table><div class="page-wrap" pagination="pagination.buildingSequenceEditor"></div></div></div><div ng-show="selectedTab === TAB_TYPES.LOGS" class="rich-text"><div class="page-wrap" pagination="pagination.logs"></div><table class="tbl-border-light tbl-striped header-center logs"><colgroup><col width="40%"><col width="30%"><col width="5%"><col width="25%"><col></colgroup><thead><tr><th>{{ 'village' | i18n:loc.ale:textObjectCommon }}</th><th>{{ 'building' | i18n:loc.ale:textObjectCommon }}</th><th>{{ 'level' | i18n:loc.ale:textObjectCommon }}</th><th>{{ 'started_at' | i18n:loc.ale:textObjectCommon }}</th></tr></thead><tbody><tr ng-show="!logsView.logs.length"><td colspan="4">{{ 'logs_no_builds' | i18n:loc.ale:textObject }}</td></tr><tr ng-repeat="log in logsView.logs"><td><span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(log[0].id)">{{ log[0].name }} ({{ log[0].x }}|{{ log[0].y }})</span></td><td><span class="building-icon icon-20x20-building-{{ log[1] }}"></span> {{ log[1] | i18n:loc.ale:'building_names' }}</td><td>{{ log[2] }}</td><td>{{ log[3] | readableDateFilter:loc.ale:GAME_TIMEZONE:GAME_TIME_OFFSET }}</td></tr></tbody></table><div class="page-wrap" pagination="pagination.logs"></div></div></div></div></div><footer class="win-foot"><ul class="list-btn list-center"><li ng-show="selectedTab === TAB_TYPES.SETTINGS && settingsView.sequencesAvail"><a href="#" class="btn-border btn-orange" ng-click="saveSettings()">{{ 'save' | i18n:loc.ale:textObjectCommon }}</a></li><li ng-show="selectedTab === TAB_TYPES.SETTINGS && settingsView.sequencesAvail"><a href="#" ng-class="{false:'btn-orange', true:'btn-red'}[running]" class="btn-border" ng-click="switchBuilder()"><span ng-show="running">{{ 'pause' | i18n:loc.ale:textObjectCommon }}</span> <span ng-show="!running">{{ 'start' | i18n:loc.ale:textObjectCommon }}</span></a></li><li ng-show="selectedTab === TAB_TYPES.LOGS"><a href="#" class="btn-border btn-orange" ng-click="logsView.clearLogs()">{{ 'logs_clear' | i18n:loc.ale:textObject }}</a></li><li ng-show="selectedTab === TAB_TYPES.SEQUENCES && editorView.sequencesAvail"><a href="#" class="btn-border btn-orange" ng-click="editorView.moveUp()">{{ 'sequences_move_up' | i18n:loc.ale:textObject }}</a></li><li ng-show="selectedTab === TAB_TYPES.SEQUENCES && editorView.sequencesAvail"><a href="#" class="btn-border btn-orange" ng-click="editorView.moveDown()">{{ 'sequences_move_down' | i18n:loc.ale:textObject }}</a></li><li ng-show="selectedTab === TAB_TYPES.SEQUENCES && editorView.sequencesAvail"><a href="#" class="btn-border btn-orange" ng-click="editorView.modal.addBuilding()">{{ 'sequences_add_building' | i18n:loc.ale:textObject }}</a></li><li ng-show="selectedTab === TAB_TYPES.SEQUENCES && editorView.sequencesAvail"><a href="#" class="btn-border btn-red" ng-click="editorView.updateBuildingSequence()">{{ 'save' | i18n:loc.ale:textObjectCommon }}</a></li></ul></footer></div>`)
          interfaceOverflow.addTemplate('twoverflow_builder_queue_add_building_modal', `<div id="add-building-modal" class="win-content" ng-init="textObject = 'builder_queue_add_building_modal'"><header class="win-head"><h3>{{ 'title' | i18n:loc.ale:textObject }}</h3><ul class="list-btn sprite"><li><a href="#" class="btn-red icon-26x26-close" ng-click="closeWindow()"></a></li></ul></header><div class="win-main" scrollbar=""><div class="box-paper"><div class="scroll-wrap unit-operate-slider"><table class="tbl-border-light tbl-striped header-center"><colgroup><col width="15%"><col><col width="15%"></colgroup><tr><td>{{ 'building' | i18n:loc.ale:'common' }}</td><td colspan="2"><div select="" list="buildings" selected="selectedBuilding" drop-down="true"></div></td></tr><tr><td>{{ 'position' | i18n:loc.ale:'builder_queue' }}</td><td><div range-slider="" min="1" max="indexLimit" value="position" enabled="true"></div></td><td><input type="text" class="input-border text-center" ng-model="position"></td></tr></table></div></div></div><footer class="win-foot sprite-fill"><ul class="list-btn list-center"><li><a href="#" class="btn-red btn-border btn-premium" ng-click="closeWindow()">{{ 'cancel' | i18n:loc.ale:'common' }}</a></li><li><a href="#" class="btn-orange btn-border" ng-click="add()">{{ 'add' | i18n:loc.ale:'common' }}</a></li></ul></footer></div>`)
          interfaceOverflow.addTemplate('twoverflow_builder_queue_name_sequence_modal', `<div id="name-sequence-modal" class="win-content" ng-init="textObject = 'builder_queue_name_sequence_modal'"><header class="win-head"><h3>{{ 'title' | i18n:loc.ale:textObject }}</h3><ul class="list-btn sprite"><li><a href="#" class="btn-red icon-26x26-close" ng-click="closeWindow()"></a></li></ul></header><div class="win-main" scrollbar=""><div class="box-paper"><div class="scroll-wrap"><div class="box-border-light input-wrapper name_preset"><form ng-submit="submit()"><input type="text" focus="true" ng-model="name" minlength="3"></form></div></div></div></div><footer class="win-foot sprite-fill"><ul class="list-btn list-center"><li><a href="#" class="btn-red btn-border btn-premium" ng-click="closeWindow()">{{ 'cancel' | i18n:loc.ale:'common' }}</a></li><li><a href="#" class="btn-orange btn-border" ng-click="submit()">{{ 'add' | i18n:loc.ale:'common' }}</a></li></ul></footer></div>`)
          interfaceOverflow.addStyle('#two-builder-queue tr.reached td{background-color:#b9af7e}#two-builder-queue tr.progress td{background-color:#af9d57}#two-builder-queue .building-sequence,#two-builder-queue .building-sequence-final,#two-builder-queue .building-sequence-editor,#two-builder-queue .logs{margin-bottom:10px}#two-builder-queue .building-sequence td,#two-builder-queue .building-sequence-final td,#two-builder-queue .building-sequence-editor td,#two-builder-queue .logs td,#two-builder-queue .building-sequence th,#two-builder-queue .building-sequence-final th,#two-builder-queue .building-sequence-editor th,#two-builder-queue .logs th{text-align:center}#two-builder-queue .building-sequence-editor .selected td{background-color:#b9af7e}#two-builder-queue .editor-select-sequence{margin-bottom:13px}#two-builder-queue a.btn{height:28px;line-height:28px;padding:0 10px}#two-builder-queue .clone-sequence{float:left;margin-right:10px}#two-builder-queue .select-sequence-editor{float:left;margin-top:1px;margin-right:10px}#two-builder-queue .remove-sequence{width:28px;height:28px}#two-builder-queue .create-sequence{padding:8px 20px 8px 20px}#add-building-modal td{text-align:center}#add-building-modal .select-wrapper{width:250px}#add-building-modal input[type="text"]{width:60px}')
      }
  
      var buildWindow = function () {
          var activeSequence = settings.getSetting(SETTINGS.ACTIVE_SEQUENCE)
  
          $scope = $rootScope.$new()
          $scope.textObject = textObject
          $scope.textObjectCommon = textObjectCommon
          $scope.selectedTab = TAB_TYPES.SETTINGS
          $scope.TAB_TYPES = TAB_TYPES
          $scope.SETTINGS = SETTINGS
          $scope.running = running
          $scope.pagination = {}
  
          $scope.editorView = editorView
          $scope.editorView.buildingSequence = {}
          $scope.editorView.visibleBuildingSequence = {}
          $scope.editorView.selectedSequence = { name: activeSequence, value: activeSequence }
  
          $scope.settingsView = settingsView
          $scope.settingsView.buildingSequence = {}
          $scope.settingsView.buildingSequenceFinal = {}
  
          $scope.logsView = logsView
          $scope.logsView.logs = builderQueue.getLogs()
  
          // methods
          $scope.selectTab = selectTab
          $scope.switchBuilder = switchBuilder
          $scope.saveSettings = saveSettings
          $scope.createFirstSequence = createFirstSequence
          $scope.openVillageInfo = windowDisplayService.openVillageInfo
  
          settings.injectSettings($scope)
          eventHandlers.updateGroups()
          eventHandlers.updateSequences()
  
          $scope.pagination.buildingSequence = {
              count: settingsView.buildingSequence.length,
              offset: 0,
              loader: settingsView.updateVisibleBuildingSequence,
              limit: storageService.getPaginationLimit()
          }
  
          $scope.pagination.buildingSequenceEditor = {
              count: editorView.buildingSequence.length,
              offset: 0,
              loader: editorView.updateVisibleBuildingSequence,
              limit: storageService.getPaginationLimit()
          }
  
          $scope.pagination.logs = {
              count: $scope.length,
              offset: 0,
              loader: logsView.updateVisibleLogs,
              limit: storageService.getPaginationLimit()
          }
  
          settingsView.generateSequences()
          editorView.generateBuildingSequence()
  
          eventScope = new EventScope('twoverflow_builder_queue_window')
          eventScope.register(eventTypeProvider.GROUPS_UPDATED, eventHandlers.updateGroups, true)
          eventScope.register(eventTypeProvider.GROUPS_CREATED, eventHandlers.updateGroups, true)
          eventScope.register(eventTypeProvider.GROUPS_DESTROYED, eventHandlers.updateGroups, true)
          eventScope.register(eventTypeProvider.VILLAGE_SELECTED_CHANGED, eventHandlers.generateBuildingSequences, true)
          eventScope.register(eventTypeProvider.BUILDING_UPGRADING, eventHandlers.generateBuildingSequences, true)
          eventScope.register(eventTypeProvider.BUILDING_LEVEL_CHANGED, eventHandlers.generateBuildingSequences, true)
          eventScope.register(eventTypeProvider.BUILDING_TEARING_DOWN, eventHandlers.generateBuildingSequences, true)
          eventScope.register(eventTypeProvider.VILLAGE_BUILDING_QUEUE_CHANGED, eventHandlers.generateBuildingSequences, true)
          eventScope.register(eventTypeProvider.BUILDER_QUEUE_JOB_STARTED, eventHandlers.updateLogs)
          eventScope.register(eventTypeProvider.BUILDER_QUEUE_CLEAR_LOGS, eventHandlers.clearLogs)
          eventScope.register(eventTypeProvider.BUILDER_QUEUE_BUILDING_SEQUENCES_UPDATED, eventHandlers.buildingSequenceUpdate)
          eventScope.register(eventTypeProvider.BUILDER_QUEUE_BUILDING_SEQUENCES_ADDED, eventHandlers.buildingSequenceAdd)
          eventScope.register(eventTypeProvider.BUILDER_QUEUE_BUILDING_SEQUENCES_REMOVED, eventHandlers.buildingSequenceRemoved)
          eventScope.register(eventTypeProvider.BUILDER_QUEUE_SETTINGS_CHANGE, eventHandlers.saveSettings)
          eventScope.register(eventTypeProvider.BUILDER_QUEUE_START, eventHandlers.started)
          eventScope.register(eventTypeProvider.BUILDER_QUEUE_STOP, eventHandlers.stopped)
  
          windowManagerService.getScreenWithInjectedScope('!twoverflow_builder_queue_window', $scope)
  
          $scope.$watch('settings[SETTINGS.ACTIVE_SEQUENCE].value', function (newValue, oldValue) {
              if (newValue !== oldValue) {
                  eventHandlers.generateBuildingSequences()
              }
          })
  
          $scope.$watch('editorView.selectedSequence.value', function (newValue, oldValue) {
              if (newValue !== oldValue) {
                  eventHandlers.generateBuildingSequencesEditor()
              }
          })
      }
  
      return init
  })
  
  define('two/builderQueue/settingsMap', [
      'two/builderQueue/defaultOrders',
      'two/builderQueue/settings'
  ], function (
      DEFAULT_ORDERS,
      SETTINGS
  ) {
      return {
          [SETTINGS.GROUP_VILLAGES]: {
              default: false,
              inputType: 'select',
              disabledOption: true,
              type: 'groups'
          },
          [SETTINGS.ACTIVE_SEQUENCE]: {
              default: 'Essential',
              inputType: 'select'
          },
          [SETTINGS.BUILDING_SEQUENCES]: {
              default: DEFAULT_ORDERS,
              inputType: 'buildingOrder'
          }
      }
  })
  
  define('two/builderQueue/settings', [], function () {
      return {
          GROUP_VILLAGES: 'group_villages',
          ACTIVE_SEQUENCE: 'building_sequence',
          BUILDING_SEQUENCES: 'building_orders',
      }
  })
  
  require([
      'two/language',
      'two/ready',
      'two/builderQueue',
      'two/builderQueue/ui',
      'two/builderQueue/events'
  ], function (
      twoLanguage,
      ready,
      builderQueue,
      builderQueueInterface
  ) {
      if (builderQueue.isInitialized()) {
          return false
      }
  
      ready(function () {
          twoLanguage.add('builder_queue', {"cs_cz":{"builder_queue":{"started":"BuilderQueue started","stopped":"BuilderQueue stopped","settings":"Settings","settings_village_groups":"Build only on villages with the group","settings_building_sequence":"Building sequence","settings_building_sequence_final":"Buildings final levels","settings_saved":"Settings saved!","logs_no_builds":"No builds started","logs_clear":"Clear logs","sequences":"Sequences","sequences_move_up":"Move up","sequences_move_down":"Move down","sequences_add_building":"Add building","sequences_select_edit":"Select a sequence to edit","sequences_edit_sequence":"Edit sequence","select_group":"Select a group","add_building_success":"%d has added at position %d","add_building_limit_exceeded":"%d reached maximum level (%d)","position":"Position","remove_building":"Remove building from list","clone":"Clone","tooltip_clone":"Crate a new sequence from the selected sequence","tooltip_remove_sequence":"Remove selected sequence","name_sequence_min_lenght":"The sequence name must have at least 3 character.","sequence_created":"New sequence %d created.","sequence_updated":"Sequence %d updated.","sequence_removed":"Sequence %d removed.","error_sequence_exists":"This sequence already exists.","error_sequence_no_exists":"This sequence not exists.","error_sequence_invalid":"Some sequence's value is invalid.","logs_cleared":"Logs cleared.","create_sequence":"Create a sequence"},"builder_queue_add_building_modal":{"title":"Add new building"},"builder_queue_name_sequence_modal":{"title":"Sequence name"},"builder_queue_remove_sequence_modal":{"title":"Remove sequence","text":"Are you sure to remove this sequence? If this sequence is the active one, another sequence will be selected and the BuilderQueue stopped."}},"de_de":{"builder_queue":{"started":"Automatischer Dorfausbau gestartet","stopped":"Automatischer Dorfausbau angehalten","settings":"Einstellungen","settings_village_groups":"Ausbau von Dörfern mit den Gruppen","settings_building_sequence":"Reihenfolge","settings_building_sequence_final":"Gebäudestufe","settings_saved":"Einstellungen gespeichert!","logs_no_builds":"Kein Ausbau gestartet","logs_clear":"Protokoll löschen","sequences":"Sequences","sequences_move_up":"Move up","sequences_move_down":"Move down","sequences_add_building":"Add building","sequences_select_edit":"Select a sequence to edit","sequences_edit_sequence":"Edit sequence","select_group":"Select a group","add_building_success":"%d has added at position %d","add_building_limit_exceeded":"%d reached maximum level (%d)","position":"Position","remove_building":"Remove building from list","clone":"Clone","tooltip_clone":"Crate a new sequence from the selected sequence","tooltip_remove_sequence":"Remove selected sequence","name_sequence_min_lenght":"The sequence name must have at least 3 character.","sequence_created":"New sequence %d created.","sequence_updated":"Sequence %d updated.","sequence_removed":"Sequence %d removed.","error_sequence_exists":"This sequence already exists.","error_sequence_no_exists":"This sequence not exists.","error_sequence_invalid":"Some sequence's value is invalid.","logs_cleared":"Logs cleared.","create_sequence":"Create a sequence"},"builder_queue_add_building_modal":{"title":"Add new building"},"builder_queue_name_sequence_modal":{"title":"Sequence name"},"builder_queue_remove_sequence_modal":{"title":"Remove sequence","text":"Are you sure to remove this sequence? If this sequence is the active one, another sequence will be selected and the BuilderQueue stopped."}},"en_us":{"builder_queue":{"started":"BuilderQueue started","stopped":"BuilderQueue stopped","settings":"Settings","settings_village_groups":"Build only on villages with the group","settings_building_sequence":"Building sequence","settings_building_sequence_final":"Buildings final levels","settings_saved":"Settings saved!","logs_no_builds":"No builds started","logs_clear":"Clear logs","sequences":"Sequences","sequences_move_up":"Move up","sequences_move_down":"Move down","sequences_add_building":"Add building","sequences_select_edit":"Select a sequence to edit","sequences_edit_sequence":"Edit sequence","select_group":"Select a group","add_building_success":"%d has added at position %d","add_building_limit_exceeded":"%d reached maximum level (%d)","position":"Position","remove_building":"Remove building from list","clone":"Clone","tooltip_clone":"Crate a new sequence from the selected sequence","tooltip_remove_sequence":"Remove selected sequence","name_sequence_min_lenght":"The sequence name must have at least 3 character.","sequence_created":"New sequence %d created.","sequence_updated":"Sequence %d updated.","sequence_removed":"Sequence %d removed.","error_sequence_exists":"This sequence already exists.","error_sequence_no_exists":"This sequence not exists.","error_sequence_invalid":"Some sequence's value is invalid.","logs_cleared":"Logs cleared.","create_sequence":"Create a sequence"},"builder_queue_add_building_modal":{"title":"Add new building"},"builder_queue_name_sequence_modal":{"title":"Sequence name"},"builder_queue_remove_sequence_modal":{"title":"Remove sequence","text":"Are you sure to remove this sequence? If this sequence is the active one, another sequence will be selected and the BuilderQueue stopped."}},"pl_pl":{"builder_queue":{"started":"BuilderQueue Uruchomiony","stopped":"BuilderQueue Zatrzymany","settings":"Ustawienia","settings_village_groups":"Buduj w wioskach z grupy","settings_building_sequence":"Szablon kolejki budowy","settings_building_sequence_final":"Finalne poziomy budynków","settings_saved":"Ustawienia zapisane!","logs_no_builds":"Nie rozpoczęto żadnej rozbudowy","logs_clear":"Wyczyść logi","sequences":"Szablony","sequences_move_up":"Przesuń w górę","sequences_move_down":"Przesuń w dół","sequences_add_building":"Dodaj budynek","sequences_select_edit":"Wybierz szablon do edytowania","sequences_edit_sequence":"Edytuj szablon","select_group":"Wybierz grupę","add_building_success":"%d dodany na pozycji %d","add_building_limit_exceeded":"%d osiągnął/eła maksymalny poziom (%d)","position":"Pozycja","remove_building":"Usuń budynek z listy","clone":"Klonuj","tooltip_clone":"Utwórz nowy szablon na podstawie wybranego szablonu","tooltip_remove_sequence":"Usuń wybrany szablon","name_sequence_min_lenght":"The sequence name must have at least 3 character.","sequence_created":"Nowy szablon %d utworzony.","sequence_updated":"Szablon %d zaktualizowany.","sequence_removed":"Szablon %d usunięty.","error_sequence_exists":"Ten szablon już istnieje.","error_sequence_no_exists":"Ten szablon nie istnieje.","error_sequence_invalid":"Niektóre z wartości szablonu są niepoprawne.","logs_cleared":"Logi wyczyszczone.","create_sequence":"Utwórz szablon"},"builder_queue_add_building_modal":{"title":"Dodaj nowy budynek"},"builder_queue_name_sequence_modal":{"title":"Nazwa szablonu"},"builder_queue_remove_sequence_modal":{"title":"Usuń szablon","text":"Jesteś pewny, że chcesz usunąć ten szablon? Jeśli ten szablon jest teraz aktywny, inny szablon zostanie wybrany i BuilderQueue zatrzyma się."}},"pt_br":{"builder_queue":{"started":"BuilderQueue iniciado","stopped":"BuilderQueue parado","settings":"Configurações","settings_village_groups":"Construir apenas em aldeias do grupo","settings_building_sequence":"Sequência de construções","settings_building_sequence_final":"Nível final das construções","settings_saved":"Configurações salvas!","logs_no_builds":"Nenhuma construção iniciada","logs_clear":"Limpar registros","sequences":"Sequências","sequences_move_up":"Mover acima","sequences_move_down":"Mover abaixo","sequences_add_building":"Adicionar edifício","sequences_select_edit":"Selecione uma sequência para editar","sequences_edit_sequence":"Editar sequência","select_group":"Selecione um grupo","add_building_success":"%d foi adicionado à posição %d","add_building_limit_exceeded":"%d chegou ao nível máximo (%d)","position":"Posição","remove_building":"Remover edifício da lista","clone":"Clonar","tooltip_clone":"Criar uma nova sequência a partir da sequência selecionada","tooltip_remove_sequence":"Remover sequência selecionada","name_sequence_min_lenght":"O nome da sequência deve ter pelo menos 3 caracteres.","sequence_created":"Nova sequência %d criada.","sequence_updated":"Sequência %d atualizada.","sequence_removed":"Sequência %d removida.","error_sequence_exists":"Esta sequência já existe.","error_sequence_no_exists":"Esta sequência não existe.","error_sequence_invalid":"Algum valor da sequência é inválido.","logs_cleared":"Registro limpo.","create_sequence":"Criar uma sequência"},"builder_queue_add_building_modal":{"title":"Adicionar novo edifício"},"builder_queue_name_sequence_modal":{"title":"Nomear sequência"},"builder_queue_remove_sequence_modal":{"title":"Remover sequência","text":"Tem certeza que deseja remover esta sequência? Se esta sequência estiver ativa, outra será selecionada e o BuilderQueue será parado."}}})
          builderQueue.init()
          builderQueueInterface()
      })
  })
  
  define('two/commandQueue', [
      'two/utils',
      'two/commandQueue/dateTypes',
      'two/commandQueue/eventCodes',
      'two/commandQueue/filterTypes',
      'two/commandQueue/commandTypes',
      'two/commandQueue/storageKeys',
      'queues/EventQueue',
      'helper/time',
      'helper/math',
      'struct/MapData',
      'Lockr'
  ], function (
      utils,
      DATE_TYPES,
      EVENT_CODES,
      FILTER_TYPES,
      COMMAND_TYPES,
      STORAGE_KEYS,
      eventQueue,
      timeHelper,
      $math,
      mapData,
      Lockr
  ) {
      var CHECKS_PER_SECOND = 10
      var ERROR_CODES = {
          INVALID_ORIGIN: 'invalid_rigin',
          INVALID_TARGET: 'invalid_target'
      }
      var waitingCommands = []
      var waitingCommandsObject = {}
      var sentCommands = []
      var expiredCommands = []
      var running = false
      var $player
      var timeOffset
  
      var commandFilters = {
          [FILTER_TYPES.SELECTED_VILLAGE]: function (command) {
              return command.origin.id === modelDataService.getSelectedVillage().getId()
          },
          [FILTER_TYPES.BARBARIAN_TARGET]: function (command) {
              return !command.target.character_id
          },
          [FILTER_TYPES.ALLOWED_TYPES]: function (command, options) {
              return options[FILTER_TYPES.ALLOWED_TYPES][command.type]
          },
          [FILTER_TYPES.ATTACK]: function (command) {
              return command.type !== COMMAND_TYPES.ATTACK
          },
          [FILTER_TYPES.SUPPORT]: function (command) {
              return command.type !== COMMAND_TYPES.SUPPORT
          },
          [FILTER_TYPES.RELOCATE]: function (command) {
              return command.type !== COMMAND_TYPES.RELOCATE
          },
          [FILTER_TYPES.TEXT_MATCH]: function (command, options) {
              var show = true
              var keywords = options[FILTER_TYPES.TEXT_MATCH].toLowerCase().split(/\W/)
  
              var searchString = [
                  command.origin.name,
                  command.origin.x + '|' + command.origin.y,
                  command.origin.character_name || '',
                  command.target.name,
                  command.target.x + '|' + command.target.y,
                  command.target.character_name || '',
                  command.target.tribe_name || '',
                  command.target.tribe_tag || ''
              ]
  
              searchString = searchString.join('').toLowerCase()
  
              keywords.some(function (keyword) {
                  if (keyword.length && !searchString.includes(keyword)) {
                      show = false
                      return true
                  }
              })
  
              return show
          }
      }
  
      var isTimeToSend = function (sendTime) {
          return sendTime < (timeHelper.gameTime() + timeOffset)
      }
  
      /**
       * Remove os zeros das unidades passadas pelo jogador.
       * A razão de remover é por que o próprio não os envia
       * quando os comandos são enviados manualmente, então
       * caso seja enviado as unidades com valores zero poderia
       * ser uma forma de detectar os comandos automáticos.
       *
       * @param  {Object} units - Unidades a serem analisadas
       * @return {Object} Objeto sem nenhum valor zero
       */
      var cleanZeroUnits = function (units) {
          var cleanUnits = {}
          var unit
          var amount
  
          for (unit in units) {
              amount = units[unit]
  
              if (amount === '*' || amount !== 0) {
                  cleanUnits[unit] = amount
              }
          }
  
          return cleanUnits
      }
  
      var sortWaitingQueue = function () {
          waitingCommands = waitingCommands.sort(function (a, b) {
              return a.sendTime - b.sendTime
          })
      }
  
      var pushWaitingCommand = function (command) {
          waitingCommands.push(command)
      }
  
      var pushCommandObject = function (command) {
          waitingCommandsObject[command.id] = command
      }
  
      var pushSentCommand = function (command) {
          sentCommands.push(command)
      }
  
      var pushExpiredCommand = function (command) {
          expiredCommands.push(command)
      }
  
      var storeWaitingQueue = function () {
          Lockr.set(STORAGE_KEYS.QUEUE_COMMANDS, waitingCommands)
      }
  
      var storeSentQueue = function () {
          Lockr.set(STORAGE_KEYS.QUEUE_SENT, sentCommands)
      }
  
      var storeExpiredQueue = function () {
          Lockr.set(STORAGE_KEYS.QUEUE_EXPIRED, expiredCommands)
      }
  
      var loadStoredCommands = function () {
          var storedQueue = Lockr.get(STORAGE_KEYS.QUEUE_COMMANDS, [], true)
  
          if (storedQueue.length) {
              for (var i = 0; i < storedQueue.length; i++) {
                  var command = storedQueue[i]
  
                  if (timeHelper.gameTime() > command.sendTime) {
                      commandQueue.expireCommand(command, EVENT_CODES.TIME_LIMIT)
                  } else {
                      waitingCommandHelpers(command)
                      pushWaitingCommand(command)
                      pushCommandObject(command)
                  }
              }
          }
      }
  
      var waitingCommandHelpers = function (command) {
          if (command.hasOwnProperty('countdown')) {
              return false
          }
  
          command.countdown = function () {
              return timeHelper.readableMilliseconds((timeHelper.gameTime() + timeOffset) - command.sendTime)
          }
      }
  
      var parseDynamicUnits = function (command) {
          var playerVillages = modelDataService.getVillages()
          var village = playerVillages[command.origin.id]
          var villageUnits
          var parsedUnits
          var unit
          var amount
  
          if (!village) {
              return EVENT_CODES.NOT_OWN_VILLAGE
          }
  
          villageUnits = village.unitInfo.units
          parsedUnits = {}
  
          for (unit in command.units) {
              amount = command.units[unit]
  
              if (amount === '*') {
                  amount = villageUnits[unit].available
  
                  if (amount === 0) {
                      continue
                  }
              } else if (amount < 0) {
                  amount = villageUnits[unit].available - Math.abs(amount)
  
                  if (amount < 0) {
                      return EVENT_CODES.NOT_ENOUGH_UNITS
                  }
              } else if (amount > 0) {
                  if (amount > villageUnits[unit].available) {
                      return EVENT_CODES.NOT_ENOUGH_UNITS
                  }
              }
  
              parsedUnits[unit] = amount
          }
  
          if (angular.equals({}, parsedUnits)) {
              return EVENT_CODES.NOT_ENOUGH_UNITS
          }
  
          return parsedUnits
      }
  
      var listenCommands = function () {
          setInterval(function () {
              if (!waitingCommands.length) {
                  return
              }
  
              waitingCommands.some(function (command) {
                  if (isTimeToSend(command.sendTime)) {
                      if (running) {
                          commandQueue.sendCommand(command)
                      } else {
                          commandQueue.expireCommand(command, EVENT_CODES.TIME_LIMIT)
                      }
                  } else {
                      return true
                  }
              })
          }, 1000 / CHECKS_PER_SECOND)
      }
  
      var commandQueue = {
          initialized: false
      }
  
      commandQueue.init = function () {
          timeOffset = utils.getTimeOffset()
          $player = modelDataService.getSelectedCharacter()
  
          commandQueue.initialized = true
  
          sentCommands = Lockr.get(STORAGE_KEYS.QUEUE_SENT, [], true)
          expiredCommands = Lockr.get(STORAGE_KEYS.QUEUE_EXPIRED, [], true)
  
          loadStoredCommands()
          listenCommands()
  
          window.addEventListener('beforeunload', function (event) {
              if (running && waitingCommands.length) {
                  event.returnValue = true
              }
          })
      }
  
      commandQueue.sendCommand = function (command) {
          var units = parseDynamicUnits(command)
  
          // units === EVENT_CODES.*
          if (typeof units === 'string') {
              return commandQueue.expireCommand(command, units)
          }
  
          command.units = units
  
          socketService.emit(routeProvider.SEND_CUSTOM_ARMY, {
              start_village: command.origin.id,
              target_village: command.target.id,
              type: command.type,
              units: command.units,
              icon: 0,
              officers: command.officers,
              catapult_target: command.catapultTarget
          })
  
          pushSentCommand(command)
          storeSentQueue()
  
          commandQueue.removeCommand(command, EVENT_CODES.COMMAND_SENT)
          eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_SEND, command)
      }
  
      commandQueue.expireCommand = function (command, eventCode) {
          pushExpiredCommand(command)
          storeExpiredQueue()
  
          commandQueue.removeCommand(command, eventCode)
      }
  
      /**
       * UPDATE THIS SHIT BELOW
       *
       * @param {Object} command
       * @param {String} command.origin - Coordenadas da aldeia de origem.
       * @param {String} command.target - Coordenadas da aldeia alvo.
       * @param {String} command.date - Data e hora que o comando deve chegar.
       * @param {String} command.dateType - Indica se o comando vai sair ou
       *   chegar na data especificada.
       * @param {Object} command.units - Unidades que serão enviados pelo comando.
       * @param {Object} command.officers - Oficiais que serão enviados pelo comando.
       * @param {String} command.type - Tipo de comando.
       * @param {String=} command.catapultTarget - Alvo da catapulta, caso o comando seja um ataque.
       */
      commandQueue.addCommand = function (command) {
          if (!command.origin) {
              return eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_ADD_INVALID_ORIGIN, command)
          }
  
          if (!command.target) {
              return eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_ADD_INVALID_TARGET, command)
          }
  
          if (!utils.isValidDateTime(command.date)) {
              return eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_ADD_INVALID_DATE, command)
          }
  
          if (!command.units || angular.equals(command.units, {})) {
              return eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_ADD_NO_UNITS, command)
          }
  
          var getOriginVillage = new Promise(function (resolve, reject) {
              commandQueue.getVillageByCoords(command.origin.x, command.origin.y, function (data) {
                  data ? resolve(data) : reject(ERROR_CODES.INVALID_ORIGIN)
              })
          })
  
          var getTargetVillage = new Promise(function (resolve, reject) {
              commandQueue.getVillageByCoords(command.target.x, command.target.y, function (data) {
                  data ? resolve(data) : reject(ERROR_CODES.INVALID_TARGET)
              })
          })
  
          var loadVillagesData = Promise.all([
              getOriginVillage,
              getTargetVillage
          ])
  
          for (var officer in command.officers) {
              if (command.officers[officer]) {
                  command.officers[officer] = 1
              } else {
                  delete command.officers[officer]
              }
          }
  
          loadVillagesData.then(function (villages) {
              command.origin = villages[0]
              command.target = villages[1]
              command.units = cleanZeroUnits(command.units)
              command.date = utils.fixDate(command.date)
              command.travelTime = utils.getTravelTime(
                  command.origin,
                  command.target,
                  command.units,
                  command.type,
                  command.officers
              )
  
              var inputTime = utils.getTimeFromString(command.date)
  
              if (command.dateType === DATE_TYPES.ARRIVE) {
                  command.sendTime = inputTime - command.travelTime
                  command.arriveTime = inputTime
              } else if (command.dateType === DATE_TYPES.OUT) {
                  command.sendTime = inputTime
                  command.arriveTime = inputTime + command.travelTime
              } else {
                  throw new Error('CommandQueue: wrong dateType:' + command.dateType)
              }
  
              if (isTimeToSend(command.sendTime)) {
                  return eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_ADD_ALREADY_SENT, command)
              }
  
              if (command.type === COMMAND_TYPES.ATTACK && 'supporter' in command.officers) {
                  delete command.officers.supporter
              }
  
              if (command.type === COMMAND_TYPES.ATTACK && command.units.catapult) {
                  command.catapultTarget = command.catapultTarget || 'headquarter'
              } else {
                  command.catapultTarget = null
              }
  
              command.id = utils.guid()
  
              waitingCommandHelpers(command)
              pushWaitingCommand(command)
              pushCommandObject(command)
              sortWaitingQueue()
              storeWaitingQueue()
  
              eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_ADD, command)
          })
  
          loadVillagesData.catch(function (errorCode) {
              switch (errorCode) {
              case ERROR_CODES.INVALID_ORIGIN:
                  eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_ADD_INVALID_ORIGIN, command)
                  break
              case ERROR_CODES.INVALID_TARGET:
                  eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_ADD_INVALID_TARGET, command)
                  break
              }
          })
      }
  
      /**
       * @param  {Object} command - Dados do comando a ser removido.
       * @param {Number} eventCode - Code indicating the reason of the remotion.
       *
       * @return {Boolean} If the command was successfully removed.
       */
      commandQueue.removeCommand = function (command, eventCode) {
          var removed = false
          delete waitingCommandsObject[command.id]
  
          for (var i = 0; i < waitingCommands.length; i++) {
              if (waitingCommands[i].id == command.id) {
                  waitingCommands.splice(i, 1)
                  storeWaitingQueue()
                  removed = true
  
                  break
              }
          }
  
          if (removed) {
              switch (eventCode) {
              case EVENT_CODES.TIME_LIMIT:
                  eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_SEND_TIME_LIMIT, command)
                  break
              case EVENT_CODES.NOT_OWN_VILLAGE:
                  eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_SEND_NOT_OWN_VILLAGE, command)
                  break
              case EVENT_CODES.NOT_ENOUGH_UNITS:
                  eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_SEND_NO_UNITS_ENOUGH, command)
                  break
              case EVENT_CODES.COMMAND_REMOVED:
                  eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_REMOVE, command)
                  break
              }
  
              return true
          } else {
              eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_REMOVE_ERROR, command)
              return false
          }
      }
  
      commandQueue.clearRegisters = function () {
          Lockr.set(STORAGE_KEYS.QUEUE_EXPIRED, [])
          Lockr.set(STORAGE_KEYS.QUEUE_SENT, [])
          expiredCommands = []
          sentCommands = []
      }
  
      commandQueue.start = function (disableNotif) {
          running = true
          eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_START, {
              disableNotif: !!disableNotif
          })
      }
  
      commandQueue.stop = function () {
          running = false
          eventQueue.trigger(eventTypeProvider.COMMAND_QUEUE_STOP)
      }
  
      commandQueue.isRunning = function () {
          return running
      }
  
      commandQueue.getWaitingCommands = function () {
          return waitingCommands
      }
  
      commandQueue.getWaitingCommandsObject = function () {
          return waitingCommandsObject
      }
  
      commandQueue.getSentCommands = function () {
          return sentCommands
      }
  
      commandQueue.getExpiredCommands = function () {
          return expiredCommands
      }
  
      commandQueue.getVillageByCoords = function (x, y, callback) {
          mapData.loadTownDataAsync(x, y, 1, 1, callback)
      }
  
      /**
       * @param  {String} filterId - Identificação do filtro.
       * @param {Array=} _options - Valores a serem passados para os filtros.
       * @param {Array=} _commandsDeepFilter - Usa os comandos passados
       * pelo parâmetro ao invés da lista de comandos completa.
       * @return {Array} Comandos filtrados.
       */
      commandQueue.filterCommands = function (filterId, _options, _commandsDeepFilter) {
          var filterHandler = commandFilters[filterId]
          var commands = _commandsDeepFilter || waitingCommands
  
          return commands.filter(function (command) {
              return filterHandler(command, _options)
          })
      }
  
      return commandQueue
  })
  
  define('two/commandQueue/commandTypes', [], function () {
      return {
          'ATTACK': 'attack',
          'SUPPORT': 'support',
          'RELOCATE': 'relocate'
      }
  })
  
  define('two/commandQueue/dateTypes', [], function () {
      return {
          ARRIVE: 'date_type_arrive',
          OUT: 'date_type_out'
      }
  })
  
  define('two/commandQueue/eventCodes', [], function () {
      return {
          NOT_OWN_VILLAGE: 'not_own_village',
          NOT_ENOUGH_UNITS: 'not_enough_units',
          TIME_LIMIT: 'time_limit',
          COMMAND_REMOVED: 'command_removed',
          COMMAND_SENT: 'command_sent'
      }
  })
  
  define('two/commandQueue/events', [], function () {
      angular.extend(eventTypeProvider, {
          COMMAND_QUEUE_SEND: 'commandqueue_send',
          COMMAND_QUEUE_SEND_TIME_LIMIT: 'commandqueue_send_time_limit',
          COMMAND_QUEUE_SEND_NOT_OWN_VILLAGE: 'commandqueue_send_not_own_village',
          COMMAND_QUEUE_SEND_NO_UNITS_ENOUGH: 'commandqueue_send_no_units_enough',
          COMMAND_QUEUE_ADD: 'commandqueue_add',
          COMMAND_QUEUE_ADD_INVALID_ORIGIN: 'commandqueue_add_invalid_origin',
          COMMAND_QUEUE_ADD_INVALID_TARGET: 'commandqueue_add_invalid_target',
          COMMAND_QUEUE_ADD_INVALID_DATE: 'commandqueue_add_invalid_date',
          COMMAND_QUEUE_ADD_NO_UNITS: 'commandqueue_add_no_units',
          COMMAND_QUEUE_ADD_ALREADY_SENT: 'commandqueue_add_already_sent',
          COMMAND_QUEUE_ADD_INVALID_ORIGIN: 'commandqueue_add_invalid_origin',
          COMMAND_QUEUE_ADD_INVALID_TARGET: 'commandqueue_add_invalid_target',
          COMMAND_QUEUE_REMOVE: 'commandqueue_remove',
          COMMAND_QUEUE_REMOVE_ERROR: 'commandqueue_remove_error',
          COMMAND_QUEUE_START: 'commandqueue_start',
          COMMAND_QUEUE_STOP: 'commandqueue_stop'
      })
  })
  
  define('two/commandQueue/filterTypes', [], function () {
      return {
          SELECTED_VILLAGE: 'selected_village',
          BARBARIAN_TARGET: 'barbarian_target',
          ALLOWED_TYPES: 'allowed_types',
          ATTACK: 'attack',
          SUPPORT: 'support',
          RELOCATE: 'relocate',
          TEXT_MATCH: 'text_match'
      }
  })
  
  define('two/commandQueue/ui', [
      'two/ui',
      'two/ui/button',
      'two/commandQueue',
      'two/EventScope',
      'two/utils',
      'two/commandQueue/dateTypes',
      'two/commandQueue/eventCodes',
      'two/commandQueue/filterTypes',
      'two/commandQueue/commandTypes',
      'two/commandQueue/storageKeys',
      'queues/EventQueue',
      'helper/time',
      'helper/util',
      'Lockr'
  ], function (
      interfaceOverflow,
      FrontButton,
      commandQueue,
      EventScope,
      utils,
      DATE_TYPES,
      EVENT_CODES,
      FILTER_TYPES,
      COMMAND_TYPES,
      STORAGE_KEYS,
      eventQueue,
      $timeHelper,
      util,
      Lockr
  ) {
      var textObject = 'command_queue'
      var textObjectCommon = 'common'
      var eventScope
      var $scope
      var $gameData = modelDataService.getGameData()
      var orderedUnitNames = $gameData.getOrderedUnitNames()
      var orderedOfficerNames = $gameData.getOrderedOfficerNames()
      var presetList = modelDataService.getPresetList()
      var mapSelectedVillage = false
      var unitOrder
      var commandData
      var TAB_TYPES = {
          ADD: 'add',
          WAITING: 'waiting',
          LOGS: 'logs'
      }
      var DEFAULT_TAB = TAB_TYPES.ADD
      var DEFAULT_CATAPULT_TARGET = 'wall'
      var attackableBuildingsList = []
      var unitList = {}
      var officerList = {}
      var timeOffset
      var activeFilters
      var filtersData
      /**
       * Name of one unity for each speed category.
       * Used to generate travel times.
       */
      var UNITS_BY_SPEED = [
          'light_cavalry',
          'heavy_cavalry',
          'archer',
          'sword',
          'ram',
          'snob',
          'trebuchet'
      ]
      var FILTER_ORDER = [
          FILTER_TYPES.SELECTED_VILLAGE,
          FILTER_TYPES.BARBARIAN_TARGET,
          FILTER_TYPES.ALLOWED_TYPES,
          FILTER_TYPES.TEXT_MATCH
      ]
  
      var setMapSelectedVillage = function (event, menu) {
          mapSelectedVillage = menu.data
      }
  
      var unsetMapSelectedVillage = function () {
          mapSelectedVillage = false
      }
  
      /**
       * @param {Number=} _ms - Optional time to be formated instead of the game date.
       * @return {String}
       */
      var formatedDate = function (_ms) {
          var date = new Date(_ms || ($timeHelper.gameTime() + utils.getTimeOffset()))
  
          var rawMS = date.getMilliseconds()
          var ms = $timeHelper.zerofill(rawMS - (rawMS % 100), 3)
          var sec = $timeHelper.zerofill(date.getSeconds(), 2)
          var min = $timeHelper.zerofill(date.getMinutes(), 2)
          var hour = $timeHelper.zerofill(date.getHours(), 2)
          var day = $timeHelper.zerofill(date.getDate(), 2)
          var month = $timeHelper.zerofill(date.getMonth() + 1, 2)
          var year = date.getFullYear()
  
          return hour + ':' + min + ':' + sec + ':' + ms + ' ' + day + '/' + month + '/' + year
      }
  
      var addDateDiff = function (date, diff) {
          if (!utils.isValidDateTime(date)) {
              return ''
          }
  
          date = utils.fixDate(date)
          date = utils.getTimeFromString(date)
          date += diff
  
          return formatedDate(date)
      }
  
      var updateTravelTimes = function () {
          $scope.isValidDate = utils.isValidDateTime(commandData.date)
  
          if (!commandData.origin || !commandData.target) {
              return false
          }
  
          var date
          var arriveTime
          var travelTime
          var sendTime
          var valueType
          var commandType
          var i
  
          for (i in COMMAND_TYPES) {
              commandType = COMMAND_TYPES[i]
              $scope.travelTimes[commandType] = {}
  
              UNITS_BY_SPEED.forEach(function (unit) {
                  travelTime = utils.getTravelTime(
                      commandData.origin,
                      commandData.target,
                      {[unit]: 1},
                      commandType,
                      commandData.officers
                  )
  
                  if ($scope.selectedDateType.value === DATE_TYPES.OUT) {
                      if ($scope.isValidDate) {
                          date = utils.fixDate(commandData.date)
                          outTime = utils.getTimeFromString(date)
                          valueType = isValidSendTime(outTime) ? 'valid' : 'invalid'
                      } else {
                          valueType = 'neutral'
                      }
                  } else if ($scope.selectedDateType.value === DATE_TYPES.ARRIVE) {
                      if ($scope.isValidDate) {
                          date = utils.fixDate(commandData.date)
                          arriveTime = utils.getTimeFromString(date)
                          sendTime = arriveTime - travelTime
                          valueType = isValidSendTime(sendTime) ? 'valid' : 'invalid'
                      } else {
                          valueType = 'invalid'
                      }
                  }
  
                  $scope.travelTimes[commandType][unit] = {
                      value: $filter('readableMillisecondsFilter')(travelTime),
                      valueType: valueType
                  }
              })
          }
      }
  
      /**
       * @param  {Number}  time - Command date input in milliseconds.
       * @return {Boolean}
       */
      var isValidSendTime = function (time) {
          if (!$scope.isValidDate) {
              return false
          }
  
          return ($timeHelper.gameTime() + timeOffset) < time
      }
  
      var updateDateType = function () {
          commandData.dateType = $scope.selectedDateType.value
          Lockr.set(STORAGE_KEYS.LAST_DATE_TYPE, $scope.selectedDateType.value)
          updateTravelTimes()
      }
  
      var insertPreset = function () {
          var selectedPreset = $scope.selectedInsertPreset.value
  
          if (!selectedPreset) {
              return false
          }
  
          var presets = modelDataService.getPresetList().getPresets()
          var preset = presets[selectedPreset]
  
          // reset displayed value
          $scope.selectedInsertPreset = {
              name: $filter('i18n')('add_insert_preset', $rootScope.loc.ale, textObject),
              value: null
          }
  
          commandData.units = angular.copy(preset.units)
          commandData.officers = angular.copy(preset.officers)
  
          if (preset.catapult_target) {
              commandData.catapultTarget = preset.catapult_target
              $scope.catapultTarget = {
                  name: $filter('i18n')(preset.catapult_target, $rootScope.loc.ale, 'building_names'),
                  value: preset.catapult_target
              }
              $scope.showCatapultSelect = true
  
          }
      }
  
      var updateWaitingCommands = function () {
          $scope.waitingCommands = commandQueue.getWaitingCommands()
      }
  
      var updateSentCommands = function () {
          $scope.sentCommands = commandQueue.getSentCommands()
      }
  
      var updateExpiredCommands = function () {
          $scope.expiredCommands = commandQueue.getExpiredCommands()
      }
  
      var updateVisibleCommands = function () {
          var commands = $scope.waitingCommands
  
          FILTER_ORDER.forEach(function (filter) {
              if ($scope.activeFilters[filter]) {
                  commands = commandQueue.filterCommands(filter, $scope.filtersData, commands)
              }
          })
  
          $scope.visibleWaitingCommands = commands
      }
  
      var onUnitInputFocus = function (unit) {
          if (commandData.units[unit] === 0) {
              commandData.units[unit] = ''
          }
      }
  
      var onUnitInputBlur = function (unit) {
          if (commandData.units[unit] === '') {
              commandData.units[unit] = 0
          }
      }
  
      var catapultTargetVisibility = function () {
          $scope.showCatapultSelect = !!commandData.units.catapult
      }
  
      var selectTab = function (tabType) {
          $scope.selectedTab = tabType
      }
  
      var addSelected = function () {
          var village = modelDataService.getSelectedVillage().data
          
          commandData.origin = {
              id: village.villageId,
              x: village.x,
              y: village.y,
              name: village.name
          }
      }
  
      var addMapSelected = function () {
          if (!mapSelectedVillage) {
              return utils.emitNotif('error', $filter('i18n')('error_no_map_selected_village', $rootScope.loc.ale, textObject))
          }
  
          commandQueue.getVillageByCoords(mapSelectedVillage.x, mapSelectedVillage.y, function (data) {
              commandData.target = {
                  id: data.id,
                  x: data.x,
                  y: data.y,
                  name: data.name
              }
          })
      }
  
      var addCurrentDate = function () {
          commandData.date = formatedDate()
      }
  
      var incrementDate = function () {
          if (!commandData.date) {
              return false
          }
  
          commandData.date = addDateDiff(commandData.date, 100)
      }
  
      var reduceDate = function () {
          if (!commandData.date) {
              return false
          }
  
          commandData.date = addDateDiff(commandData.date, -100)
      }
  
      var cleanUnitInputs = function () {
          commandData.units = angular.copy(unitList)
          commandData.officers = angular.copy(officerList)
          commandData.catapultTarget = DEFAULT_CATAPULT_TARGET
          $scope.catapultTarget = {
              name: $filter('i18n')(DEFAULT_CATAPULT_TARGET, $rootScope.loc.ale, 'building_names'),
              value: DEFAULT_CATAPULT_TARGET
          }
          $scope.showCatapultSelect = false
      }
  
      var addCommand = function (type) {
          var copy = angular.copy(commandData)
          copy.type = type
  
          commandQueue.addCommand(copy)
      }
  
      var clearRegisters = function () {
          commandQueue.clearRegisters()
          updateSentCommands()
          updateExpiredCommands()
      }
  
      var switchCommandQueue = function () {
          if (commandQueue.isRunning()) {
              commandQueue.stop()
          } else {
              commandQueue.start()
          }
      }
  
      /**
       * Gera um texto de notificação com as traduções.
       *
       * @param  {String} key
       * @param  {String} key2
       * @param  {String=} prefix
       * @return {String}
       */
      var genNotifText = function (key, key2, prefix) {
          if (prefix) {
              key = prefix + '.' + key
          }
  
          var a = $filter('i18n')(key, $rootScope.loc.ale, textObject)
          var b = $filter('i18n')(key2, $rootScope.loc.ale, textObject)
  
          return a + ' ' + b
      }
  
      var toggleFilter = function (filter, allowedTypes) {
          $scope.activeFilters[filter] = !$scope.activeFilters[filter]
  
          if (allowedTypes) {
              $scope.filtersData[FILTER_TYPES.ALLOWED_TYPES][filter] = !$scope.filtersData[FILTER_TYPES.ALLOWED_TYPES][filter]
          }
  
          updateVisibleCommands()
      }
  
      var textMatchFilter = function () {
          $scope.activeFilters[FILTER_TYPES.TEXT_MATCH] = $scope.filtersData[FILTER_TYPES.TEXT_MATCH].length > 0
          updateVisibleCommands()
      }
  
      var eventHandlers = {
          updatePresets: function () {
              $scope.presets = utils.obj2selectOptions(presetList.getPresets())
          },
          autoCompleteSelected: function (event, id, data, type) {
              if (id !== 'commandqueue_village_search') {
                  return false
              }
  
              commandData[type] = {
                  id: data.raw.id,
                  x: data.raw.x,
                  y: data.raw.y,
                  name: data.raw.name
              }
  
              $scope.searchQuery[type] = ''
          },
          addInvalidOrigin: function (event) {
              utils.emitNotif('error', $filter('i18n')('error_origin', $rootScope.loc.ale, textObject))
          },
          addInvalidTarget: function (event) {
              utils.emitNotif('error', $filter('i18n')('error_target', $rootScope.loc.ale, textObject))
          },
          addInvalidDate: function (event) {
              utils.emitNotif('error', $filter('i18n')('error_invalid_date', $rootScope.loc.ale, textObject))
          },
          addNoUnits: function (event) {
              utils.emitNotif('error', $filter('i18n')('error_no_units', $rootScope.loc.ale, textObject))
          },
          addAlreadySent: function (event, command) {
              var commandType = $filter('i18n')(command.type, $rootScope.loc.ale, textObjectCommon)
              var date = utils.formatDate(command.sendTime)
  
              utils.emitNotif('error', $filter('i18n')('error_already_sent', $rootScope.loc.ale, textObject, commandType, date))
          },
          removeCommand: function (event, command) {
              updateWaitingCommands()
              updateVisibleCommands()
              $rootScope.$broadcast(eventTypeProvider.TOOLTIP_HIDE, 'twoverflow-tooltip')
              utils.emitNotif('success', genNotifText(command.type, 'removed'))
          },
          removeError: function (event, command) {
              utils.emitNotif('error', $filter('i18n')('error_remove_error', $rootScope.loc.ale, textObject))
          },
          sendTimeLimit: function (event, command) {
              updateSentCommands()
              updateExpiredCommands()
              updateWaitingCommands()
              updateVisibleCommands()
              utils.emitNotif('error', genNotifText(command.type, 'expired'))
          },
          sendNotOwnVillage: function (event, command) {
              updateSentCommands()
              updateExpiredCommands()
              updateWaitingCommands()
              updateVisibleCommands()
              utils.emitNotif('error', $filter('i18n')('error_not_own_village', $rootScope.loc.ale, textObject))
          },
          sendNoUnitsEnough: function (event, command) {
              updateSentCommands()
              updateExpiredCommands()
              updateWaitingCommands()
              updateVisibleCommands()
              utils.emitNotif('error', $filter('i18n')('error_no_units_enough', $rootScope.loc.ale, textObject))
          },
          addCommand: function (event, command) {
              updateWaitingCommands()
              updateVisibleCommands()
              utils.emitNotif('success', genNotifText(command.type, 'added'))
          },
          sendCommand: function (event, command) {
              updateSentCommands()
              updateWaitingCommands()
              updateVisibleCommands()
              utils.emitNotif('success', genNotifText(command.type, 'sent'))
          },
          start: function (event, data) {
              $scope.running = commandQueue.isRunning()
  
              if (data.disableNotif) {
                  return false
              }
  
              utils.emitNotif('success', genNotifText('title', 'activated'))
          },
          stop: function (event) {
              $scope.running = commandQueue.isRunning()
              utils.emitNotif('success', genNotifText('title', 'deactivated'))
          },
          onAutoCompleteOrigin: function (data) {
              commandData.origin = {
                  id: data.id,
                  x: data.x,
                  y: data.y,
                  name: data.name
              }
          },
          onAutoCompleteTarget: function (data) {
              commandData.target = {
                  id: data.id,
                  x: data.x,
                  y: data.y,
                  name: data.name
              }
          }
      }
  
      var init = function () {
          timeOffset = utils.getTimeOffset()
          var attackableBuildingsMap = $gameData.getAttackableBuildings()
          var building
  
          for (building in attackableBuildingsMap) {
              attackableBuildingsList.push({
                  name: $filter('i18n')(building, $rootScope.loc.ale, 'building_names'),
                  value: building
              })
          }
  
          unitOrder = angular.copy(orderedUnitNames)
          unitOrder.splice(unitOrder.indexOf('catapult'), 1)
  
          orderedUnitNames.forEach(function (unit) {
              unitList[unit] = 0
          })
  
          orderedOfficerNames.forEach(function (unit) {
              officerList[unit] = false
          })
  
          commandData = {
              origin: false,
              target: false,
              date: '',
              dateType: DATE_TYPES.OUT,
              units: angular.copy(unitList),
              officers: angular.copy(officerList),
              catapultTarget: DEFAULT_CATAPULT_TARGET,
              type: null
          }
          activeFilters = {
              [FILTER_TYPES.SELECTED_VILLAGE]: false,
              [FILTER_TYPES.BARBARIAN_TARGET]: false,
              [FILTER_TYPES.ALLOWED_TYPES]: true,
              [FILTER_TYPES.ATTACK]: true,
              [FILTER_TYPES.SUPPORT]: true,
              [FILTER_TYPES.RELOCATE]: true,
              [FILTER_TYPES.TEXT_MATCH]: false
          }
          filtersData = {
              [FILTER_TYPES.ALLOWED_TYPES]: {
                  [FILTER_TYPES.ATTACK]: true,
                  [FILTER_TYPES.SUPPORT]: true,
                  [FILTER_TYPES.RELOCATE]: true,
              },
              [FILTER_TYPES.TEXT_MATCH]: ''
          }
  
          opener = new FrontButton('Commander', {
              classHover: false,
              classBlur: false,
              onClick: buildWindow
          })
  
          eventQueue.register(eventTypeProvider.COMMAND_QUEUE_START, function () {
              opener.$elem.classList.remove('btn-green')
              opener.$elem.classList.add('btn-red')
          })
  
          eventQueue.register(eventTypeProvider.COMMAND_QUEUE_STOP, function () {
              opener.$elem.classList.remove('btn-red')
              opener.$elem.classList.add('btn-green')
          })
  
          $rootScope.$on(eventTypeProvider.SHOW_CONTEXT_MENU, setMapSelectedVillage)
          $rootScope.$on(eventTypeProvider.DESTROY_CONTEXT_MENU, unsetMapSelectedVillage)
  
          interfaceOverflow.addTemplate('twoverflow_queue_window', `<div id="two-command-queue" class="win-content two-window"><header class="win-head"><h2>CommandQueue</h2><ul class="list-btn"><li><a href="#" class="size-34x34 btn-red icon-26x26-close" ng-click="closeWindow()"></a></li></ul></header><div class="win-main small-select" scrollbar=""><div class="tabs tabs-bg"><div class="tabs-three-col"><div class="tab" ng-click="selectTab(TAB_TYPES.ADD)" ng-class="{true:'tab-active', false:''}[selectedTab == TAB_TYPES.ADD]"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.ADD}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.ADD}">{{ 'tab_add' | i18n:loc.ale:textObject }}</a></div></div></div><div class="tab" ng-click="selectTab(TAB_TYPES.WAITING)" ng-class="{true:'tab-active', false:''}[selectedTab == TAB_TYPES.WAITING]"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.WAITING}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.WAITING}">{{ 'tab_waiting' | i18n:loc.ale:textObject }}</a></div></div></div><div class="tab" ng-click="selectTab(TAB_TYPES.LOGS)" ng-class="{true:'tab-active', false:''}[selectedTab == TAB_TYPES.LOGS]"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.LOGS}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.LOGS}">{{ 'tab_logs' | i18n:loc.ale:textObject }}</a></div></div></div></div></div><div class="box-paper footer"><div class="scroll-wrap"><div class="add" ng-show="selectedTab === TAB_TYPES.ADD"><form class="addForm"><div><table class="tbl-border-light tbl-striped"><colgroup><col width="30%"><col width="5%"><col><col width="18%"></colgroup><tbody><tr><td><div auto-complete="autoCompleteOrigin"></div></td><td class="text-center"><span class="icon-26x26-rte-village"></span></td><td ng-if="!commandData.origin">{{ 'add_no_village' | i18n:loc.ale:textObject }}</td><td ng-if="commandData.origin">{{ commandData.origin.name }} ({{ commandData.origin.x }}|{{ commandData.origin.y }})</td><td class="actions"><a class="btn btn-orange" ng-click="addSelected()" tooltip="" tooltip-content="{{ 'add_selected' | i18n:loc.ale:textObject }}">{{ 'selected' | i18n:loc.ale:textObjectCommon }}</a></td></tr><tr><td><div auto-complete="autoCompleteTarget"></div></td><td class="text-center"><span class="icon-26x26-rte-village"></span></td><td ng-if="!commandData.target">{{ 'add_no_village' | i18n:loc.ale:textObject }}</td><td ng-if="commandData.target">{{ commandData.target.name }} ({{ commandData.target.x }}|{{ commandData.target.y }})</td><td class="actions"><a class="btn btn-orange" ng-click="addMapSelected()" tooltip="" tooltip-content="{{ 'add_map_selected' | i18n:loc.ale:textObject }}">{{ 'selected' | i18n:loc.ale:textObjectCommon }}</a></td></tr><tr><td><input ng-model="commandData.date" type="text" class="textfield-border date" pattern="\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(:\\d{1,3})? \\d{1,2}\\/\\d{1,2}\\/\\d{4}\\s*" placeholder="{{ 'add_date' | i18n:loc.ale:textObject }}" tooltip="" tooltip-content="hh:mm:ss:SSS dd/MM/yyyy"></td><td class="text-center"><span class="icon-26x26-time"></span></td><td><div select="" list="dateTypes" selected="selectedDateType" drop-down="true"></div></td><td class="actions"><a class="btn btn-orange" ng-click="reduceDate()" tooltip="" tooltip-content="{{ 'add_current_date_minus' | i18n:loc.ale:textObject }}">-</a><a class="btn btn-orange" ng-click="addCurrentDate()" tooltip="" tooltip-content="{{ 'add_current_date' | i18n:loc.ale:textObject }}">{{ 'now' | i18n:loc.ale:textObjectCommon }}</a><a class="btn btn-orange" ng-click="incrementDate()" tooltip="" tooltip-content="{{ 'add_current_date_plus' | i18n:loc.ale:textObject }}">+</a></td></tr></tbody></table><table ng-show="commandData.origin && commandData.target" class="tbl-border-light tbl-units tbl-speed screen-village-info"><thead><tr><th colspan="7">{{ 'speed_title' | i18n:loc.ale:textObjectVillageInfo }}</th></tr></thead><tbody><tr><td class="odd"><div class="unit-wrap"><span class="icon icon-34x34-unit-knight" tooltip="" tooltip-content="{{ 'knight' | i18n:loc.ale:textObjectUnitNames }}"></span> <span class="icon icon-34x34-unit-light_cavalry" tooltip="" tooltip-content="{{ 'light_cavalry' | i18n:loc.ale:textObjectUnitNames }}"></span> <span class="icon icon-34x34-unit-mounted_archer" tooltip="" tooltip-content="{{ 'mounted_archer' | i18n:loc.ale:textObjectUnitNames }}"></span></div><div><div class="box-time-sub-icon time-support" ng-class="{'text-red': travelTimes.support.light_cavalry.valueType == 'invalid', 'text-green-bright': travelTimes.support.light_cavalry.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_support' | i18n:loc.ale:textObjectMilitaryOperations }}"><div class="time-icon icon-20x20-support-check"></div>{{ travelTimes.support.light_cavalry.value }}</div><div class="box-time-sub-icon time-attack" ng-class="{'text-red': travelTimes.attack.light_cavalry.valueType == 'invalid', 'text-green-bright': travelTimes.attack.light_cavalry.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_attack' | i18n:loc.ale:textObjectMilitaryOperations }}"><div class="time-icon icon-20x20-attack-check"></div>{{ travelTimes.attack.light_cavalry.value }}</div><div class="box-time-sub-icon time-relocate" ng-class="{'text-red': travelTimes.relocate.light_cavalry.valueType == 'invalid', 'text-green-bright': travelTimes.relocate.light_cavalry.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_relocate' | i18n:loc.ale:textObjectMilitaryOperations }}"><div class="time-icon icon-20x20-relocate"></div>{{ travelTimes.relocate.light_cavalry.value }}</div></div></td><td><div class="unit-wrap"><span class="icon icon-single icon-34x34-unit-heavy_cavalry" tooltip="" tooltip-content="{{ 'heavy_cavalry' | i18n:loc.ale:textObjectUnitNames }}"></span></div><div><div class="box-time-sub time-support" ng-class="{'text-red': travelTimes.support.heavy_cavalry.valueType == 'invalid', 'text-green-bright': travelTimes.support.heavy_cavalry.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_support' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.support.heavy_cavalry.value }}</div><div class="box-time-sub time-attack" ng-class="{'text-red': travelTimes.support.heavy_cavalry.valueType == 'invalid', 'text-green-bright': travelTimes.support.heavy_cavalry.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_attack' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.attack.heavy_cavalry.value }}</div><div class="box-time-sub time-relocate" ng-class="{'text-red': travelTimes.support.heavy_cavalry.valueType == 'invalid', 'text-green-bright': travelTimes.support.heavy_cavalry.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_relocate' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.relocate.heavy_cavalry.value }}</div></div></td><td class="odd"><div class="unit-wrap"><span class="icon icon-34x34-unit-archer" tooltip="" tooltip-content="{{ 'archer' | i18n:loc.ale:textObjectUnitNames }}"></span> <span class="icon icon-34x34-unit-spear" tooltip="" tooltip-content="{{ 'spear' | i18n:loc.ale:textObjectUnitNames }}"></span> <span class="icon icon-34x34-unit-axe" tooltip="" tooltip-content="{{ 'axe' | i18n:loc.ale:textObjectUnitNames }}"></span> <span class="icon icon-34x34-unit-doppelsoldner" tooltip="" tooltip-content="{{ 'doppelsoldner' | i18n:loc.ale:textObjectUnitNames }}"></span></div><div><div class="box-time-sub time-support" ng-class="{'text-red': travelTimes.support.archer.valueType == 'invalid', 'text-green-bright': travelTimes.support.archer.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_support' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.support.archer.value }}</div><div class="box-time-sub time-attack" ng-class="{'text-red': travelTimes.support.archer.valueType == 'invalid', 'text-green-bright': travelTimes.support.archer.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_attack' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.attack.archer.value }}</div><div class="box-time-sub time-relocate" ng-class="{'text-red': travelTimes.support.archer.valueType == 'invalid', 'text-green-bright': travelTimes.support.archer.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_relocate' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.relocate.archer.value }}</div></div></td><td><div class="unit-wrap"><span class="icon icon-single icon-34x34-unit-sword" tooltip="" tooltip-content="{{ 'sword' | i18n:loc.ale:textObjectUnitNames }}"></span></div><div><div class="box-time-sub time-support" ng-class="{'text-red': travelTimes.support.sword.valueType == 'invalid', 'text-green-bright': travelTimes.support.sword.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_support' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.support.sword.value }}</div><div class="box-time-sub time-attack" ng-class="{'text-red': travelTimes.support.sword.valueType == 'invalid', 'text-green-bright': travelTimes.support.sword.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_attack' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.attack.sword.value }}</div><div class="box-time-sub time-relocate" ng-class="{'text-red': travelTimes.support.sword.valueType == 'invalid', 'text-green-bright': travelTimes.support.sword.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_relocate' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.relocate.sword.value }}</div></div></td><td class="odd"><div class="unit-wrap"><span class="icon icon-34x34-unit-catapult" tooltip="" tooltip-content="{{ 'catapult' | i18n:loc.ale:textObjectUnitNames }}"></span> <span class="icon icon-34x34-unit-ram" tooltip="" tooltip-content="{{ 'ram' | i18n:loc.ale:textObjectUnitNames }}"></span></div><div><div class="box-time-sub time-support" ng-class="{'text-red': travelTimes.support.ram.valueType == 'invalid', 'text-green-bright': travelTimes.support.ram.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_support' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.support.ram.value }}</div><div class="box-time-sub time-attack" ng-class="{'text-red': travelTimes.support.ram.valueType == 'invalid', 'text-green-bright': travelTimes.support.ram.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_attack' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.attack.ram.value }}</div><div class="box-time-sub time-relocate" ng-class="{'text-red': travelTimes.support.ram.valueType == 'invalid', 'text-green-bright': travelTimes.support.ram.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_relocate' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.relocate.ram.value }}</div></div></td><td><div class="unit-wrap"><span class="icon icon-single icon-34x34-unit-snob" tooltip="" tooltip-content="{{ 'snob' | i18n:loc.ale:textObjectUnitNames }}"></span></div><div><div class="box-time-sub time-support" ng-class="{'text-red': travelTimes.support.snob.valueType == 'invalid', 'text-green-bright': travelTimes.support.snob.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_support' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.support.snob.value }}</div><div class="box-time-sub time-attack" ng-class="{'text-red': travelTimes.support.snob.valueType == 'invalid', 'text-green-bright': travelTimes.support.snob.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_attack' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.attack.snob.value }}</div><div class="box-time-sub time-relocate" ng-class="{'text-red': travelTimes.support.snob.valueType == 'invalid', 'text-green-bright': travelTimes.support.snob.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'cannot_relocate_snob' | i18n:loc.ale:textObjectMilitaryOperations }}">-</div></div></td><td class="odd"><div class="unit-wrap"><span class="icon icon-single icon-34x34-unit-trebuchet" tooltip="" tooltip-content="{{ 'trebuchet' | i18n:loc.ale:textObjectUnitNames }}"></span></div><div><div class="box-time-sub time-support" ng-class="{'text-red': travelTimes.support.trebuchet.valueType == 'invalid', 'text-green-bright': travelTimes.support.trebuchet.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_support' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.support.trebuchet.value }}</div><div class="box-time-sub time-attack" ng-class="{'text-red': travelTimes.support.trebuchet.valueType == 'invalid', 'text-green-bright': travelTimes.support.trebuchet.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_attack' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.attack.trebuchet.value }}</div><div class="box-time-sub time-relocate" ng-class="{'text-red': travelTimes.support.trebuchet.valueType == 'invalid', 'text-green-bright': travelTimes.support.trebuchet.valueType == 'valid'}" tooltip="" tooltip-content="{{ 'travel_time_relocate' | i18n:loc.ale:textObjectMilitaryOperations }}">{{ travelTimes.relocate.trebuchet.value }}</div></div></td></tr></tbody></table></div><h5 class="twx-section">{{ 'units' | i18n:loc.ale:textObjectCommon }}</h5><table class="tbl-border-light tbl-striped"><colgroup><col width="25%"><col width="25%"><col width="25%"><col width="25%"></colgroup><tbody class="add-units"><tr><td colspan="4" class="actions"><ul class="list-btn list-center"><li><div select="" list="presets" selected="selectedInsertPreset" drop-down="true"></div></li><li><a class="clear-units btn btn-orange" ng-click="cleanUnitInputs()">{{ 'add_clear' | i18n:loc.ale:textObject }}</a></li></ul></td></tr><tr ng-repeat="i in [] | range:(unitOrder.length / 4);"><td class="cell-space-left"><span class="icon-bg-black" ng-class="'icon-34x34-unit-' + unitOrder[i * 4]" tooltip="" tooltip-content="{{ unitOrder[i * 4] | i18n:loc.ale:textObjectUnitNames }}"></span> <input remove-zero="" type="text" ng-model="commandData.units[unitOrder[i * 4]]" maxlength="5" placeholder="{{ commandData.units[unitOrder[i * 4]] }}" ng-focus="onUnitInputFocus(unitOrder[i * 4])" ng-blur="onUnitInputBlur(unitOrder[i * 4])"></td><td class="cell-space-left"><span class="icon-bg-black" ng-class="'icon-34x34-unit-' + unitOrder[i * 4 + 1]" tooltip="" tooltip-content="{{ unitOrder[i * 4 + 1] | i18n:loc.ale:textObjectUnitNames }}"></span> <input remove-zero="" type="text" ng-model="commandData.units[unitOrder[i * 4 + 1]]" maxlength="5" placeholder="{{ commandData.units[unitOrder[i * 4 + 1]] }}" ng-focus="onUnitInputFocus(unitOrder[i * 4 + 1])" ng-blur="onUnitInputBlur(unitOrder[i * 4 + 1])"></td><td class="cell-space-left"><span class="icon-bg-black" ng-class="'icon-34x34-unit-' + unitOrder[i * 4 + 2]" tooltip="" tooltip-content="{{ unitOrder[i * 4 + 2] | i18n:loc.ale:textObjectUnitNames }}"></span> <input remove-zero="" type="text" ng-model="commandData.units[unitOrder[i * 4 + 2]]" maxlength="5" placeholder="{{ commandData.units[unitOrder[i * 4 + 2]] }}" ng-focus="onUnitInputFocus(unitOrder[i * 4 + 2])" ng-blur="onUnitInputBlur(unitOrder[i * 4 + 2])"></td><td class="cell-space-left"><span class="icon-bg-black" ng-class="'icon-34x34-unit-' + unitOrder[i * 4 + 3]" tooltip="" tooltip-content="{{ unitOrder[i * 4 + 3] | i18n:loc.ale:textObjectUnitNames }}"></span> <input remove-zero="" type="text" ng-model="commandData.units[unitOrder[i * 4 + 3]]" maxlength="5" placeholder="{{ commandData.units[unitOrder[i * 4 + 3]] }}" ng-focus="onUnitInputFocus(unitOrder[i * 4 + 3])" ng-blur="onUnitInputBlur(unitOrder[i * 4 + 3])"></td></tr><tr><td class="cell-space-left"><span class="icon-bg-black icon-34x34-unit-catapult" tooltip="" tooltip-content="{{ 'catapult' | i18n:loc.ale:textObjectUnitNames }}"></span> <input remove-zero="" type="text" ng-model="commandData.units.catapult" maxlength="5" placeholder="{{ commandData.units.catapult }}" ng-keyup="catapultTargetVisibility()" ng-focus="onUnitInputFocus('catapult')" ng-blur="onUnitInputBlur('catapult')"></td><td class="cell-space-left" colspan="3"><div ng-visible="showCatapultSelect"><div class="unit-border box-slider"><div class="height-wrapper"><div select="" list="attackableBuildings" selected="catapultTarget"></div></div></div></div></td></tr></tbody></table><h5 class="twx-section">{{ 'officers' | i18n:loc.ale:textObjectCommon }}</h5><table class="add-officers margin-top tbl-border-light tbl-officers"><tbody><tr><td class="cell-officers" ng-repeat="officer in officers"><table class="tbl-border-dark tbl-officer"><tbody><tr><td class="cell-space"><span class="icon-44x44-premium_officer_{{ officer }}"></span></td><td class="cell-officer-switch" rowspan="2"><div switch-slider="" enabled="true" value="commandData.officers[officer]" vertical="true" size="'34x66'"></div></td></tr><tr><td tooltip="" tooltip-content="{{ 'available_officers' | i18n:loc.ale:'modal_preset_edit' }}"><div class="amount">{{ inventory.getItemAmountByType('premium_officer_' + officer) | number }}</div></td></tr></tbody></table></td></tr></tbody></table></form></div><div class="waiting rich-text" ng-show="selectedTab === TAB_TYPES.WAITING"><div class="filters"><table class="tbl-border-light"><tbody><tr><td><div ng-class="{'active': activeFilters[FILTER_TYPES.SELECTED_VILLAGE]}" ng-click="toggleFilter(FILTER_TYPES.SELECTED_VILLAGE)" class="box-border-dark icon selectedVillage" tooltip="" tooltip-content="{{ 'filters_selected_village' | i18n:loc.ale:textObject }}"><span class="icon-34x34-village-info icon-bg-black"></span></div><div ng-class="{'active': activeFilters[FILTER_TYPES.BARBARIAN_TARGET]}" ng-click="toggleFilter(FILTER_TYPES.BARBARIAN_TARGET)" class="box-border-dark icon barbarianTarget" tooltip="" tooltip-content="{{ 'filters_barbarian_target' | i18n:loc.ale:textObject }}"><span class="icon-34x34-barbarian-village icon-bg-black"></span></div><div ng-class="{'active': activeFilters[FILTER_TYPES.ATTACK]}" ng-click="toggleFilter(FILTER_TYPES.ATTACK, true)" class="box-border-dark icon allowedTypes" tooltip="" tooltip-content="{{ 'filters_attack' | i18n:loc.ale:textObject }}"><span class="icon-34x34-attack icon-bg-black"></span></div><div ng-class="{'active': activeFilters[FILTER_TYPES.SUPPORT]}" ng-click="toggleFilter(FILTER_TYPES.SUPPORT, true)" class="box-border-dark icon allowedTypes" tooltip="" tooltip-content="{{ 'filters_support' | i18n:loc.ale:textObject }}"><span class="icon-34x34-support icon-bg-black"></span></div><div ng-class="{'active': activeFilters[FILTER_TYPES.RELOCATE]}" ng-click="toggleFilter(FILTER_TYPES.RELOCATE, true)" class="box-border-dark icon allowedTypes" tooltip="" tooltip-content="{{ 'filters_relocate' | i18n:loc.ale:textObject }}"><span class="icon-34x34-relocate icon-bg-black"></span></div><div class="text"><input ng-model="filtersData[FILTER_TYPES.TEXT_MATCH]" type="text" class="box-border-dark" placeholder="{{ 'filters_text_match' | i18n:loc.ale:textObject }}"></div></td></tr></tbody></table></div><div class="queue"><h5 class="twx-section">{{ 'queue_waiting' | i18n:loc.ale:textObject }}</h5><p class="text-center" ng-show="!visibleWaitingCommands.length">{{ 'queue_none_added' | i18n:loc.ale:textObject }}</p><table class="tbl-border-light" ng-repeat="command in visibleWaitingCommands"><colgroup><col width="100px"></colgroup><tbody><tr><th colspan="2"><span ng-class="{true: 'icon-bg-red', false:'icon-bg-blue'}[command.type === COMMAND_TYPES.ATTACK]" class="icon-26x26-{{ command.type }}" tooltip="" tooltip-content="{{ command.type | i18n:loc.ale:textObjectCommon }}"></span> <span class="size-26x26 icon-bg-black icon-26x26-time-duration" tooltip="" tooltip-content="{{ 'command_time_left' | i18n:loc.ale:textObject }}"></span> <span class="time-left">{{ command.countdown() }}</span> <span class="size-26x26 icon-bg-black icon-20x20-units-outgoing" tooltip="" tooltip-content="{{ 'command_out' | i18n:loc.ale:textObject }}"></span> <span class="sent-time">{{ command.sendTime | readableDateFilter:loc.ale }}</span> <span class="size-26x26 icon-bg-black icon-20x20-time-arrival" tooltip="" tooltip-content="{{ 'command_arrive' | i18n:loc.ale:textObject }}"></span> <span class="arrive-time">{{ command.arriveTime | readableDateFilter:loc.ale }}</span> <a href="#" class="remove-command size-20x20 btn-red icon-20x20-close" ng-click="removeCommand(command, EVENT_CODES.COMMAND_REMOVED)" tooltip="" tooltip-content="{{ 'queue_remove' | i18n:loc.ale:textObject }}"></a></th></tr><tr><td>{{ 'villages' | i18n:loc.ale:textObjectCommon }}</td><td><a class="origin"><span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(command.origin.id)">{{ command.origin.name }} ({{ command.origin.x }}|{{ command.origin.y }})</span></a> <span class="size-20x20 icon-26x26-{{ command.type }}"></span> <a class="target"><span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(command.target.id)">{{ command.target.name }} ({{ command.target.x }}|{{ command.target.y }})</span></a></td></tr><tr><td>{{ 'units' | i18n:loc.ale:textObjectCommon }}</td><td class="units"><div class="unit" ng-repeat="(unit, amount) in command.units"><span class="icon-34x34-unit-{{ unit }} icon"></span> <span class="amount">{{ amount }}</span> <span ng-if="unit === 'catapult' && command.type === COMMAND_TYPES.ATTACK">({{ command.catapultTarget | i18n:loc.ale:textObjectCommon }})</span></div><div class="officer" ng-repeat="(officer, enabled) in command.officers"><span class="icon-34x34-premium_officer_{{ officer }}"></span></div></td></tr></tbody></table></div></div><div class="logs rich-text" ng-show="selectedTab === TAB_TYPES.LOGS"><h5 class="twx-section">{{ 'queue_sent' | i18n:loc.ale:textObject }}</h5><p class="text-center" ng-show="!sentCommands.length">{{ 'queue_none_sent' | i18n:loc.ale:textObject }}</p><table class="tbl-border-light" ng-repeat="command in sentCommands track by $index"><colgroup><col width="100px"></colgroup><tbody><tr><th colspan="2"><span ng-class="{true: 'icon-bg-red', false:'icon-bg-blue'}[command.type === COMMAND_TYPES.ATTACK]" class="icon-26x26-{{ command.type }}" tooltip="" tooltip-content="{{ command.type | i18n:loc.ale:textObjectCommon }}"></span> <span class="size-26x26 icon-bg-black icon-20x20-units-outgoing" tooltip="" tooltip-content="{{ 'command_out' | i18n:loc.ale:textObject }}"></span> <span class="sent-time">{{ command.sendTime | readableDateFilter:loc.ale }}</span> <span class="size-26x26 icon-bg-black icon-20x20-time-arrival" tooltip="" tooltip-content="{{ 'command_arrive' | i18n:loc.ale:textObject }}"></span> <span class="arrive-time">{{ command.arriveTime | readableDateFilter:loc.ale }}</span></th></tr><tr><td>{{ 'villages' | i18n:loc.ale:textObjectCommon }}</td><td><a class="origin"><span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(command.origin.id)">{{ command.origin.name }} ({{ command.origin.x }}|{{ command.origin.y }})</span></a> <span class="size-20x20 icon-26x26-{{ command.type }}"></span> <a class="target"><span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(command.target.id)">{{ command.target.name }} ({{ command.target.x }}|{{ command.target.y }})</span></a></td></tr><tr><td>{{ 'units' | i18n:loc.ale:textObjectCommon }}</td><td class="units"><div class="unit" ng-repeat="(unit, amount) in command.units"><span class="icon-34x34-unit-{{ unit }} icon"></span> <span class="amount">{{ amount }}</span> <span ng-if="unit === 'catapult' && command.type === COMMAND_TYPES.ATTACK">({{ command.catapultTarget | i18n:loc.ale:textObjectCommon }})</span></div><div class="officer" ng-repeat="(officer, enabled) in command.officers"><span class="icon-34x34-premium_officer_{{ officer }}"></span></div></td></tr></tbody></table><h5 class="twx-section">{{ 'queue_expired' | i18n:loc.ale:textObject }}</h5><p class="text-center" ng-show="!expiredCommands.length">{{ 'queue_none_expired' | i18n:loc.ale:textObject }}</p><table class="tbl-border-light" ng-repeat="command in expiredCommands track by $index"><colgroup><col width="100px"></colgroup><tbody><tr><th colspan="2"><span ng-class="{true: 'icon-bg-red', false:'icon-bg-blue'}[command.type === COMMAND_TYPES.ATTACK]" class="icon-26x26-{{ command.type }}" tooltip="" tooltip-content="{{ command.type | i18n:loc.ale:textObjectCommon }}"></span> <span class="size-26x26 icon-bg-black icon-20x20-units-outgoing" tooltip="" tooltip-content="{{ 'command_out' | i18n:loc.ale:textObject }}"></span> <span class="sent-time">{{ command.sendTime | readableDateFilter:loc.ale }}</span> <span class="size-26x26 icon-bg-black icon-20x20-time-arrival" tooltip="" tooltip-content="{{ 'command_arrive' | i18n:loc.ale:textObject }}"></span> <span class="arrive-time">{{ command.arriveTime | readableDateFilter:loc.ale }}</span></th></tr><tr><td>{{ 'villages' | i18n:loc.ale:textObjectCommon }}</td><td><a class="origin"><span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(command.origin.id)">{{ command.origin.name }} ({{ command.origin.x }}|{{ command.origin.y }})</span></a> <span class="size-20x20 icon-26x26-{{ command.type }}"></span> <a class="target"><span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(command.target.id)">{{ command.target.name }} ({{ command.target.x }}|{{ command.target.y }})</span></a></td></tr><tr><td>{{ 'units' | i18n:loc.ale:textObjectCommon }}</td><td class="units"><div class="unit" ng-repeat="(unit, amount) in command.units"><span class="icon-34x34-unit-{{ unit }} icon"></span> <span class="amount">{{ amount }}</span> <span ng-if="unit === 'catapult' && command.type === COMMAND_TYPES.ATTACK">({{ command.catapultTarget | i18n:loc.ale:textObjectCommon }})</span></div><div class="officer" ng-repeat="(officer, enabled) in command.officers"><span class="icon-34x34-premium_officer_{{ officer }}"></span></div></td></tr></tbody></table></div></div></div></div><footer class="win-foot"><ul class="list-btn list-center"><li ng-show="selectedTab === TAB_TYPES.LOGS"><a class="btn-orange btn-border" ng-click="clearRegisters()">{{ 'general_clear' | i18n:loc.ale:textObject }}</a></li><li ng-show="selectedTab === TAB_TYPES.ADD"><a class="btn-orange btn-border add" ng-click="addCommand(COMMAND_TYPES.ATTACK)"><span class="icon-26x26-attack-small"></span> {{ COMMAND_TYPES.ATTACK | i18n:loc.ale:textObjectCommon }}</a></li><li ng-show="selectedTab === TAB_TYPES.ADD"><a class="btn-orange btn-border add" ng-click="addCommand(COMMAND_TYPES.SUPPORT)"><span class="icon-26x26-support"></span> {{ COMMAND_TYPES.SUPPORT | i18n:loc.ale:textObjectCommon }}</a></li><li ng-show="selectedTab === TAB_TYPES.ADD"><a class="btn-orange btn-border add" ng-click="addCommand(COMMAND_TYPES.RELOCATE)"><span class="icon-26x26-relocate"></span> {{ COMMAND_TYPES.RELOCATE | i18n:loc.ale:textObjectCommon }}</a></li><li><a href="#" ng-class="{false:'btn-green', true:'btn-red'}[running]" class="btn-border" ng-click="switchCommandQueue()"><span ng-show="running">{{ 'deactivate' | i18n:loc.ale:textObjectCommon }}</span> <span ng-show="!running">{{ 'activate' | i18n:loc.ale:textObjectCommon }}</span></a></li></ul></footer></div>`)
          interfaceOverflow.addStyle('#two-command-queue input[type="text"]{width:200px}#two-command-queue input.unit{width:80px;height:34px}#two-command-queue form .padded{padding:2px 8px}#two-command-queue a.select-handler{-webkit-box-shadow:none;box-shadow:none;height:22px;line-height:22px}#two-command-queue a.select-button{height:22px}#two-command-queue .custom-select{width:240px}#two-command-queue .originVillage,#two-command-queue .targetVillage{padding:0 7px}#two-command-queue a.btn{height:28px;line-height:28px;padding:0 10px}#two-command-queue .actions{text-align:center}#two-command-queue .add-units td{text-align:center}#two-command-queue .add-units .unit-icon{top:-1px}#two-command-queue .add-units input{height:34px;line-height:26px;color:#fff3d0;font-size:14px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAABGdBTUEAALGPC/xhBQAAALRQTFRFr6+vmJiYoKCgrKysq6urpaWltLS0s7OzsLCwpKSkm5ubqKiojY2NlZWVk5OTqampbGxsWFhYUVFRhISEgYGBmpqaUFBQnp6eYmJidnZ2nZ2dY2NjW1tbZ2dnoaGhe3t7l5eXg4ODVVVVWVlZj4+PXFxcVlZWkpKSZmZmdXV1ZWVlc3NzjIyMXl5eVFRUeHh4hoaGYWFhXV1dbW1tampqb29veXl5fHx8gICAiYmJcnJyTk5Ooj6l1wAAADx0Uk5TGhkZGhoaGxoaGRkaGRkZGhkbHBgYGR0ZGhkZGhsZGRgZGRwbGRscGRoZGhkZGhwZGRobGRkZGRkZGRkeyXExWQAABOJJREFUSMeNVgdy4zgQxIW9TQ7KOVEUo5gz0f//1/WA0sple6+OLokQiUk9PQ2rvlzvT0vA6xDXU3R5hQmqddDVaIELsMl3KLUGoFHugUphjt25PWkE6KMAqPkO/Qh7HRadPmTNxKJpWuhSjLZAoSZmXYoPXh0w2R2z10rjBxpMNRfomhbNFUfUFbfUCh6TWmO4ZqNn6Jxekx6lte3h9IgYv9ZwzIZXfhQ/bejmsYkgOeVInoDGT6KGP9MMbsj7mtEKphKgVFKkJGUM+r/00zybNkPMFWYske+jY9hUblbrK4YosyPtrxl+5kNRWSb2B3+pceKT05SQRPZY8pVSGoWutgen2junRVKPZJ0v5Nu9HAk/CFPr+T1XTkXYFWSJXfTyLPcpcPXtBZIPONq/cFQ0Y0Lr1GF6f5doHdm2RLTbQMpMmCIf/HGm53OLFPiiEOsBKtgHccgKTVwn8l7kbt3iPvqniMX4jgWj4aqlX43xLwXVet5XTG1cYp/29m58q6ULSa7V0M3UQFyjd+AD+1W9WLBpDd9uej7emFbea/+Yw8faySElQQrBDksTpTOVIG/SE2HpPvZsplJWsblRLEGXATEW9YLUY1rPSdivBDmuK3exNiAysfPALfYZFWJrsA4Zt+fftEeRY0UsMDqfyNCKJpdrtI1r2k0vp9LMSwdO0u5SpjBeEYz5ebhWNbwT2g7OJXy1vjW+pEwyd1FTkAtbzzcbmX1yZlkR2pPiXZ/mDbPNWvHRsaKfLH8+FqiZbnodbOK9RGWlNMli8k+wsgbSNwS35QB6qxn53xhu2DFqUilisB9q2Zqw4nNI9tOB2z8GbkvEdNjPaD2j+9pwEC+YlWJvI7xN7xMC09eqhq/qwRvz3JWcFWmkjrWBWSiOysEmc4LmMb0iSsxR8+Z8pk3+oE39cdAmh1xSDXuAryRLZgpp9V62+8IOeBSICjs8LlbtKGN4E7XGoGASIJ+vronVa5mjagPHIFJA2b+BKkZC5I/78wOqmzYp1N8vzTkWIWz6YfsS3eh3w8pBkfKz6TSLxK9Qai5DUGTMZ8NNmrW8ldNudIJq+eJycwjv+xbeOJwPv1jjsSV/rCBaS/IBrafaUQ+5ksHwwl9y9X7kmvvIKWoBDFvbWySGyMU3XflxZRkNeRU63otWb0+P8H8BrRokbJivpWkk6m6LccSlrC2K0i6+4otx4dN3mbAVKt0wbaqBab4/MW8rgrS8JP06HU6UYSTYsQ5pYETpo87ZonORvbPlvYbXwmsMgoQGKr8PUQ5dDEO0EcXp2oOfSk+YpR/Eg4R46O0/Sf7jVnbqbXBrRkCPsZFOQTN8h+aqlcRw9FjJ/j8V7SXZ3hVNXYsOYcxzpfPNgFrvB9S6Dej2PqDqq0su+5ng0WMi527p/pA+OiW0fsYzDa6sPS9C1qxTtxVRMuySrwPD6qGPRKc4uIx4oceJ9FPjxWaqPPebzyXxU7W1jNqqOw+9z6X/k+Na3SBa0v+VjgoaULR30G1nxvZN1vsha2UaSrKy/PyCaHK5zAYnJzm9RSpSPDWbDVu0dkUujMmB/ly4w8EnDdXXoyX/VfhB3yKzMJ2BSaZO+A9GiNQMbll+6z1WGLWpEGMeEg85MESSep0IPFaHYZZ1QOW/xcjfxGhNjP0tRtbhFHOmhhjAv/p77JrCX3+ZAAAAAElFTkSuQmCC) top left #b89064;box-shadow:inset 0 0 0 1px #000,inset 0 0 0 2px #a2682c,inset 0 0 0 3px #000,inset -3px -3px 2px 0 #fff,inset 0 0 9px 5px rgba(99,54,0,0.5);text-align:center;width:80px}#two-command-queue .add-officers .cell-officers{padding:7px 11px 5px 11px}#two-command-queue .add-officers .amount{color:#fff;text-align:center}#two-command-queue .command{margin-bottom:10px}#two-command-queue .command .time-left{width:93px;display:inline-block;padding:0 0 0 3px}#two-command-queue .command .sent-time,#two-command-queue .command .arrive-time{width:160px;display:inline-block;padding:0 0 0 5px}#two-command-queue .command td{padding:3px 6px}#two-command-queue .officers td{width:111px;text-align:center}#two-command-queue .officers label{margin-left:5px}#two-command-queue .officers span{margin-left:2px}#two-command-queue .units div.unit{float:left}#two-command-queue .units div.unit span.icon{transform:scale(.7);width:25px;height:25px}#two-command-queue .units div.unit span.amount{vertical-align:-2px;margin:0 5px 0 2px}#two-command-queue .units div.officer{float:left;margin:0 2px}#two-command-queue .units div.officer span{transform:scale(.7);width:25px;height:25px}#two-command-queue .remove-command{float:right;margin-top:3px}#two-command-queue .tbl-units td{text-align:center}#two-command-queue .tbl-speed{margin-top:10px}#two-command-queue .tbl-speed th{text-align:center}#two-command-queue .tbl-speed td{font-size:12px}#two-command-queue .tbl-speed .box-time-sub-icon{position:relative}#two-command-queue .tbl-speed .box-time-sub-icon .time-icon{position:absolute;top:-3px;left:27px;transform:scale(.7)}#two-command-queue .tbl-speed .box-time-sub-icon.time-relocate .time-icon{top:-6px;left:26px}#two-command-queue .dateType{width:200px}#two-command-queue .dateType .custom-select-handler{text-align:left}#two-command-queue .filters .icon{width:38px;float:left;margin:0 6px}#two-command-queue .filters .icon.active:before{box-shadow:0 0 0 1px #000,-1px -1px 0 2px #ac9c44,0 0 0 3px #ac9c44,0 0 0 4px #000;border-radius:1px;content:"";position:absolute;width:38px;height:38px;left:-1px;top:-1px}#two-command-queue .filters .text{margin-left:262px}#two-command-queue .filters .text input{height:36px;margin-top:1px;width:100%;text-align:left;padding:0 5px}#two-command-queue .filters .text input::placeholder{color:white}#two-command-queue .filters .text input:focus::placeholder{color:transparent}#two-command-queue .filters td{padding:6px}#two-command-queue .icon-34x34-barbarian-village:before{filter:grayscale(100%);background-image:url(https://i.imgur.com/ozI4k0h.png);background-position:-220px -906px}#two-command-queue .icon-20x20-time-arrival:before{transform:scale(.8);background-image:url(https://i.imgur.com/ozI4k0h.png);background-position:-529px -454px}#two-command-queue .icon-20x20-attack:before{transform:scale(.8);background-image:url(https://i.imgur.com/ozI4k0h.png);background-position:-546px -1086px;width:26px;height:26px}#two-command-queue .icon-20x20-support:before{transform:scale(.8);background-image:url(https://i.imgur.com/ozI4k0h.png);background-position:-462px -360px;width:26px;height:26px}#two-command-queue .icon-20x20-relocate:before{transform:scale(.8);background-image:url(https://i.imgur.com/ozI4k0h.png);background-position:-1090px -130px;width:26px;height:26px}#two-command-queue .icon-26x26-attack:before{background-image:url(https://i.imgur.com/ozI4k0h.png);background-position:-546px -1086px}')
      }
  
      var buildWindow = function () {
          var lastDateType = Lockr.get(STORAGE_KEYS.LAST_DATE_TYPE, DATE_TYPES.OUT, true)
  
          $scope = $rootScope.$new()
          $scope.textObject = textObject
          $scope.textObjectCommon = textObjectCommon
          $scope.textObjectVillageInfo = 'screen_village_info'
          $scope.textObjectUnitNames = 'unit_names'
          $scope.textObjectMilitaryOperations = 'military_operations'
          $scope.selectedTab = DEFAULT_TAB
          $scope.inventory = modelDataService.getInventory()
          $scope.presets = utils.obj2selectOptions(presetList.getPresets())
          $scope.travelTimes = {}
          $scope.unitOrder = unitOrder
          $scope.officers = $gameData.getOrderedOfficerNames()
          $scope.searchQuery = {
              origin: '',
              target: ''
          }
          $scope.isValidDate = false
          $scope.dateTypes = util.toActionList(DATE_TYPES, function (actionType) {
              return $filter('i18n')(actionType, $rootScope.loc.ale, textObject)
          })
          $scope.selectedDateType = {
              name: $filter('i18n')(lastDateType, $rootScope.loc.ale, textObject),
              value: lastDateType
          }
          $scope.selectedInsertPreset = {
              name: $filter('i18n')('add_insert_preset', $rootScope.loc.ale, textObject),
              value: null
          }
          $scope.catapultTarget = {
              name: $filter('i18n')(DEFAULT_CATAPULT_TARGET, $rootScope.loc.ale, 'building_names'),
              value: DEFAULT_CATAPULT_TARGET
          }
          $scope.autoCompleteOrigin = {
              type: ['village'],
              placeholder: $filter('i18n')('add_village_search', $rootScope.loc.ale, textObject),
              onEnter: eventHandlers.onAutoCompleteOrigin,
              tooltip: $filter('i18n')('add_origin', $rootScope.loc.ale, textObject),
              dropDown: true
          }
          $scope.autoCompleteTarget = {
              type: ['village'],
              placeholder: $filter('i18n')('add_village_search', $rootScope.loc.ale, textObject),
              onEnter: eventHandlers.onAutoCompleteTarget,
              tooltip: $filter('i18n')('add_target', $rootScope.loc.ale, textObject),
              dropDown: true
          }
          $scope.showCatapultSelect = !!commandData.units.catapult
          $scope.attackableBuildings = attackableBuildingsList
          $scope.commandData = commandData
          $scope.activeFilters = activeFilters
          $scope.filtersData = filtersData
          $scope.running = commandQueue.isRunning()
          $scope.waitingCommands = commandQueue.getWaitingCommands()
          $scope.visibleWaitingCommands = commandQueue.getWaitingCommands()
          $scope.sentCommands = commandQueue.getSentCommands()
          $scope.expiredCommands = commandQueue.getExpiredCommands()
          $scope.EVENT_CODES = EVENT_CODES
          $scope.FILTER_TYPES = FILTER_TYPES
          $scope.TAB_TYPES = TAB_TYPES
          $scope.COMMAND_TYPES = COMMAND_TYPES
  
          // functions
          $scope.onUnitInputFocus = onUnitInputFocus
          $scope.onUnitInputBlur = onUnitInputBlur
          $scope.catapultTargetVisibility = catapultTargetVisibility
          $scope.selectTab = selectTab
          $scope.addSelected = addSelected
          $scope.addMapSelected = addMapSelected
          $scope.addCurrentDate = addCurrentDate
          $scope.incrementDate = incrementDate
          $scope.reduceDate = reduceDate
          $scope.cleanUnitInputs = cleanUnitInputs
          $scope.addCommand = addCommand
          $scope.clearRegisters = clearRegisters
          $scope.switchCommandQueue = switchCommandQueue
          $scope.removeCommand = commandQueue.removeCommand
          $scope.openVillageInfo = windowDisplayService.openVillageInfo
          $scope.toggleFilter = toggleFilter
  
          $scope.$watch('commandData.origin', updateTravelTimes)
          $scope.$watch('commandData.target', updateTravelTimes)
          $scope.$watch('commandData.date', updateTravelTimes)
          $scope.$watch('selectedDateType.value', updateDateType)
          $scope.$watch('selectedInsertPreset.value', insertPreset)
          $scope.$watch('filtersData[FILTER_TYPES.TEXT_MATCH]', textMatchFilter)
  
          eventScope = new EventScope('twoverflow_queue_window')
          eventScope.register(eventTypeProvider.ARMY_PRESET_UPDATE, eventHandlers.updatePresets, true)
          eventScope.register(eventTypeProvider.ARMY_PRESET_DELETED, eventHandlers.updatePresets, true)
          eventScope.register(eventTypeProvider.SELECT_SELECTED, eventHandlers.autoCompleteSelected, true)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_ADD_INVALID_ORIGIN, eventHandlers.addInvalidOrigin)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_ADD_INVALID_TARGET, eventHandlers.addInvalidTarget)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_ADD_INVALID_DATE, eventHandlers.addInvalidDate)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_ADD_NO_UNITS, eventHandlers.addNoUnits)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_ADD_ALREADY_SENT, eventHandlers.addAlreadySent)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_REMOVE, eventHandlers.removeCommand)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_REMOVE_ERROR, eventHandlers.removeError)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_SEND_TIME_LIMIT, eventHandlers.sendTimeLimit)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_SEND_NOT_OWN_VILLAGE, eventHandlers.sendNotOwnVillage)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_SEND_NO_UNITS_ENOUGH, eventHandlers.sendNoUnitsEnough)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_ADD, eventHandlers.addCommand)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_SEND, eventHandlers.sendCommand)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_START, eventHandlers.start)
          eventScope.register(eventTypeProvider.COMMAND_QUEUE_STOP, eventHandlers.stop)
  
          windowManagerService.getScreenWithInjectedScope('!twoverflow_queue_window', $scope)
      }
  
      return init
  })
  
  define('two/commandQueue/storageKeys', [], function () {
      return {
          QUEUE_COMMANDS: 'command_queue_commands',
          QUEUE_SENT: 'command_queue_sent',
          QUEUE_EXPIRED: 'command_queue_expired',
          LAST_DATE_TYPE: 'command_queue_last_date_type'
      }
  })
  
  require([
      'two/language',
      'two/ready',
      'two/commandQueue',
      'two/commandQueue/ui',
      'two/commandQueue/events'
  ], function (
      twoLanguage,
      ready,
      commandQueue,
      commandQueueInterface
  ) {
      if (commandQueue.initialized) {
          return false
      }
  
      ready(function () {
          twoLanguage.add('command_queue', {"cs_cz":{"command_queue":{"title":"ŘadaPříkazů","attack":"útok","support":"podpora","relocate":"přesun","sent":"sent","activated":"aktivováno","deactivated":"zrušeno","expired":"vypršelo","removed":"odstraněno","added":"přidáno","general_clear":"Vyčistit registr","general_next_command":"Další příkaz","add_basics":"Základní informace","add_origin":"Počáteční souřadnice","add_selected":"Aktivní vesnice","add_target":"Cílové souřadnice","add_map_selected":"Vyberte vesnici na mapě","date_type_arrive":"Čas příchodu","date_type_out":"Čas odchodu","add_current_date":"Momentální čas","add_current_date_plus":"Increase date in 100 milliseconds.","add_current_date_minus":"Reduce date in 100 milliseconds.","add_travel_times":"Doba cesty","add_date":"Date/time","add_no_village":"select a village...","add_village_search":"Village search...","add_clear":"Clear fields","add_insert_preset":"Insert preset","queue_waiting":"Vyčkávající příkazy","queue_none_added":"Žádné přidané příkazy.","queue_sent":"Commands sent","queue_none_sent":"No command sent.","queue_expired":"Expirované příkazy","queue_none_expired":"Žádné expirované příkazy.","queue_remove":"Odstranit příkaz z listu","queue_filters":"Filtrovat příkaz","filters_selected_village":"Ukázat příkazy pouze z vybrané vesnice","filters_barbarian_target":"Ukázat příkazy pouze s barbarskými vesnicemi za cíl","filters_attack":"Ukázat útoky","filters_support":"Ukázat podpory","filters_relocate":"Ukázat přesun","filters_text_match":"Filtrovat dle textu...","command_out":"Odchozí","command_time_left":"Zbývající čas","command_arrive":"Příchod","error_no_units_enough":"Odeslat příkaz bez nedostatku jednotek!","error_not_own_village":"The origin village is not owned by you!","error_origin":"Souřeadnice počáteční vesnice jsou chybné!","error_target":"Souřadnice cílové vesnice jsou chybné!","error_no_units":"Nebyly vybrání jednotky!","error_invalid_date":"Chbný čas","error_already_sent":"Tyto jednotky: %{type} odejdou v %{date}","error_no_map_selected_village":"Není vybrána vesnice na mapě.","error_remove_error":"Chyba při odstranění příkazu.","tab_add":"Add command","tab_waiting":"Queued commands","tab_logs":"Command logs"}},"de_de":{"command_queue":{"title":"Befehlsschleife","attack":"angreifen","support":"unterstützen","relocate":"umsiedeln","sent":"gesendet","activated":"aktiviert","deactivated":"deaktiviert","expired":"abgelaufen","removed":"entfernt","added":"hinzugefügt","general_clear":"Protokoll löschen","general_next_command":"Nächster Befehl","add_basics":"Informationen","add_origin":"Startkoordinaten","add_selected":"Aktives Dorf","add_target":"Zielkoordinaten","add_map_selected":"Ausgewähltes Dorf","date_type_arrive":"Eintreffzeitpunkt","date_type_out":"Startzeitpunkt","add_current_date":"Jetzt","add_current_date_plus":"Zeitpunkt um 100 Millisekunden erhöhen.","add_current_date_minus":"Zeitpunkt um 100 Millisekunden verringern.","add_travel_times":"Laufzeit","add_date":"Zeitpunkt","add_no_village":"Dorf auswählen...","add_village_search":"Dorf suchen...","add_clear":"Felder leeren","add_insert_preset":"Vorlagen einfügen","queue_waiting":"Wartende Befehle","queue_none_added":"Es wurden keine Befehle hinzugefügt.","queue_sent":"Befehle gesendet","queue_none_sent":"Kein Befehl gesendet.","queue_expired":"Abgelaufene Befehle","queue_none_expired":"Keine abgelaufenen Befehle.","queue_remove":"Befehl aus Liste entfernen","queue_filters":"Filter","filters_selected_village":"Zeige nur Befehle bezüglich des aktuellen Dorfs","filters_barbarian_target":"Zeige nur Befehle mit Barbarendörfer als Ziel","filters_attack":"Zeige Angriffe","filters_support":"Zeige Unterstützungen","filters_relocate":"Zeige Umsiedlungen","filters_text_match":"Filtern nach Text...","command_out":"Ausgehend","command_time_left":"Zeit verbleibend","command_arrive":"Ankunftszeit","error_no_units_enough":"Nicht genug Einheiten für den Befehl!","error_not_own_village":"Du bist nicht im Besitz des Startdorfes!","error_origin":"Startkoordinaten ungültig!","error_target":"Zielkoordinaten ungültig!","error_no_units":"Keine Einheiten ausgewählt!","error_invalid_date":"Ungültiges Datum","error_already_sent":"Diese %{type} hätten um %{date} rausgehen müssen","error_no_map_selected_village":"Kein Dorf auf der Karte ausgewählt.","error_remove_error":"Fehler beim Entfernen des Befehls.","tab_add":"Add command","tab_waiting":"Queued commands","tab_logs":"Command logs"}},"en_us":{"command_queue":{"title":"CommandQueue","attack":"attack","support":"support","relocate":"transfer","sent":"sent","activated":"activated","deactivated":"disabled","expired":"expired","removed":"removed","added":"added","general_clear":"Clear logs","general_next_command":"Next command","add_basics":"Basic information","add_origin":"Origin coordinates","add_selected":"Active village","add_target":"Target coordinates","add_map_selected":"Selected village on a map","date_type_arrive":"Command arrive at date","date_type_out":"Command leave at date","add_current_date":"Current date","add_current_date_plus":"Increase date in 100 milliseconds.","add_current_date_minus":"Reduce date in 100 milliseconds.","add_travel_times":"Travel time","add_date":"Date/time","add_no_village":"select a village...","add_village_search":"Village search...","add_clear":"Clear fields","add_insert_preset":"Insert preset","queue_waiting":"Waiting commands","queue_none_added":"No command added.","queue_sent":"Commands sent","queue_none_sent":"No command sent.","queue_expired":"Expired commands","queue_none_expired":"No command expired.","queue_remove":"Remove command form list","queue_filters":"Commands filter","filters_selected_village":"Show only commands from the selected village","filters_barbarian_target":"Show only commands with barbarian villages as target","filters_attack":"Show attacks","filters_support":"Show supports","filters_relocate":"Show transfers","filters_text_match":"Filter by text...","command_out":"Out","command_time_left":"Time remaining","command_arrive":"Arrival","error_no_units_enough":"No units enough to send the command!","error_not_own_village":"The origin village is not owned by you!","error_origin":"Origin village coordinates invalid!","error_target":"Target village coordinates invalid!","error_no_units":"No units specified!","error_invalid_date":"Invalid date","error_already_sent":"This %d should have left %d","error_no_map_selected_village":"No selected village on the map.","error_remove_error":"Error removing command.","tab_add":"Add command","tab_waiting":"Queued commands","tab_logs":"Command logs"}},"pl_pl":{"command_queue":{"title":"CommandQueue","attack":"Atak","support":"Wsparcie","relocate":"przenieś","sent":"wysłany/e","activated":"włączony","deactivated":"wyłączony","expired":"przedawniony/e","removed":"usunięty/e","added":"dodany/e","general_clear":"Wyczyść logi","general_next_command":"Następny rozkaz","add_basics":"Podstawowe informacje","add_origin":"Źródło","add_selected":"Aktywna wioska","add_target":"Cel","add_map_selected":"Wybrana wioska na mapie","date_type_arrive":"Czas dotarcia na cel","date_type_out":"Czas wyjścia z twojej wioski","add_current_date":"Obecny czas","add_current_date_plus":"Zwiększ czas o 100 milisekund.","add_current_date_minus":"Zmniejsz czas o 100 milisekund.","add_travel_times":"Czas podróży jednostek","add_date":"Czas/Data","add_no_village":"Wybierz wioskę...","add_village_search":"Znajdź wioskę...","add_clear":"wyczyść","add_insert_preset":"Wybierz szablon","queue_waiting":"Rozkazy","queue_none_added":"Brak dodanych rozkazów.","queue_sent":"Rozkazy wysłane","queue_none_sent":"Brak wysłanych rozkazów.","queue_expired":"Przedawnione rozkazy","queue_none_expired":"Brak przedawnionych rozkazów.","queue_remove":"Usuń rozkaz z listy","queue_filters":"Filtruj rozkazy","filters_selected_village":"Pokaż tylko rozkazy z aktywnej wioski","filters_barbarian_target":"Pokaż tylko rozkazy na wioski barbarzyńskie","filters_attack":"Pokaż ataki","filters_support":"Pokaż wsparcia","filters_relocate":"Pokaż przeniesienia","filters_text_match":"Filtruj za pomocą tekstu...","command_out":"Czas wyjścia","command_time_left":"Pozostały czas","command_arrive":"Czas dotarcia","error_no_units_enough":"Brak wystarczającej liczby jednostek do wysłania rozkazu!","error_not_own_village":"Wioska źródłowa nie należy do ciebie!","error_origin":"Nieprawidłowa wioska źródłowa!","error_target":"Nieprawidłowa wioska cel!","error_no_units":"Nie wybrano jednostek!","error_invalid_date":"Nieprawidłowy Czas","error_already_sent":"Ten rozkaz %d powinien zostać wysłany %d","error_no_map_selected_village":"Nie zaznaczono wioski na mapie.","error_remove_error":"Błąd usuwania rozkazu.","tab_add":"Dodaj rozkaz","tab_waiting":"Oczekujące","tab_logs":"Logi"}},"pt_br":{"command_queue":{"title":"CommandQueue","attack":"Ataque","support":"Apoio","relocate":"Transferência","sent":"enviado","activated":"ativado","deactivated":"desativado","expired":"expirado","removed":"removido","added":"adicionado","general_clear":"Limpar registros","general_next_command":"Próximo comando","add_basics":"Informações básicas","add_origin":"Coordenadas da origem","add_selected":"Aldeia ativa","add_target":"Coordenadas do alvo","add_map_selected":"Aldeia selecionada no mapa","date_type_arrive":"Data de chegada","date_type_out":"Data de saída","add_current_date":"Data/hora","add_current_date_plus":"Aumentar data em 100 milisegunds.","add_current_date_minus":"Reduzir data em 100 milisegunds.","add_travel_times":"Tempos de viagem","add_date":"Data","add_no_village":"selecione uma aldeia...","add_village_search":"Procurar aldeia...","add_clear":"Limpar campos","add_insert_preset":"Inserir predefinição","queue_waiting":"Comandos em espera","queue_none_added":"Nenhum comando adicionado.","queue_sent":"Comandos enviados","queue_none_sent":"Nenhum comando enviado.","queue_expired":"Comandos expirados","queue_none_expired":"Nenhum comando expirado.","queue_remove":"Remover comando da lista","queue_filters":"Filtro de comandos","filters_selected_village":"Mostrar apenas comandos com origem da aldeia selecionada","filters_barbarian_target":"Mostrar apenas comandos com aldeias bárbaras como alvo","filters_attack":"Mostrar ataques","filters_support":"Mostrar apoios","filters_relocate":"Mostrar transferências","filters_text_match":"Filtrar por texto...","command_out":"Saída na data","command_time_left":"Tempo restante","command_arrive":"Chegada na data","error_no_units_enough":"Sem unidades o sulficiente para enviar o comando!","error_not_own_village":"A aldeia de origem não pertence a você!","error_origin":"Aldeia de origem inválida!","error_target":"Aldeia alvo inválida!","error_no_units":"Nenhuma unidade especificada!","error_invalid_date":"Data inválida","error_already_sent":"Esse %d deveria ter saído %d","error_no_map_selected_village":"Nenhuma aldeia selecionada no mapa.","error_remove_error":"Erro ao remover comando.","tab_add":"Adicionar comando","tab_waiting":"Comandos em espera","tab_logs":"Registro de comandos"}}})
          commandQueue.init()
          commandQueueInterface()
  
          if (commandQueue.getWaitingCommands().length > 0) {
              commandQueue.start(true)
          }
      })
  })
  
  const simulateMouseEvent = function (element, eventName, coordX, coordY) {
      element.dispatchEvent(new MouseEvent(eventName, {
          view: window,
          bubbles: true,
          cancelable: true,
          clientX: coordX,
          clientY: coordY,
          button: 0
      }));
  };  
  const klikam = function () {
      const gra = document.querySelector('#click-layer');
      simulateMouseEvent(gra, 'mousedown', 35, 452);
      simulateMouseEvent(gra, 'mouseup', 35, 452);
      simulateMouseEvent(gra, 'click', 35, 452);
  };
  define('two/farmOverflow', [
      'two/Settings',
      'two/farmOverflow/types/errors',
      'two/farmOverflow/types/status',
      'two/farmOverflow/settings',
      'two/farmOverflow/settings/map',
      'two/farmOverflow/settings/updates',
      'two/farmOverflow/types/logs',
      'two/mapData',
      'two/utils',
      'two/ready',
      'helper/math',
      'helper/time',
      'queues/EventQueue',
      'conf/commandTypes',
      'conf/village',
      'conf/resourceTypes',
      'struct/MapData',
      'Lockr'
  ], function (
      Settings,
      ERROR_TYPES,
      STATUS,
      SETTINGS,
      SETTINGS_MAP,
      UPDATES,
      LOG_TYPES,
      twoMapData,
      utils,
      ready,
      math,
      timeHelper,
      eventQueue,
      COMMAND_TYPES,
      VILLAGE_CONFIG,
      RESOURCE_TYPES,
      $mapData,
      Lockr
  ) {
      let initialized = false;
      let running = false;
      let settings;
      let farmSettings;
      const farmers = [];
      let logs = [];
      let includedVillages = [];
      let ignoredVillages = [];
      let onlyVillages = [];
      let selectedPresets = [];
      let activeFarmer = false;
      let sendingCommand = false;
      let currentTarget = false;
      let farmerIndex = 0;
      let cycleTimer = null;
      let stepDelayTimer = null;
      let commandExpireTimer = null;
      let exceptionLogs;
      const tempVillageReports = {};
      let $player;
      let unitsData;
      let persistentRunningLastCheck = timeHelper.gameTime();
      let persistentRunningTimer = null;
      let nextCycleDate = null;
      const PERSISTENT_RUNNING_CHECK_INTERVAL = 30 * 1000;
      const VILLAGE_COMMAND_LIMIT = 50;
      const MINIMUM_FARMER_CYCLE_INTERVAL = 1; // minutes
      const MINIMUM_ATTACK_INTERVAL = 0; // seconds
      const STEP_EXPIRE_TIME = 30 * 1000;
      const CYCLE_BEGIN = 'cycle_begin';
      const IGNORE_UPDATES = 'ignore_update';
      const STORAGE_KEYS = {
          LOGS: 'farm_overflow_logs',
          SETTINGS: 'farm_overflow_settings',
          EXCEPTION_LOGS: 'farm_overflow_exception_logs'
      };
      const RESOURCES = [
          RESOURCE_TYPES.WOOD,
          RESOURCE_TYPES.CLAY,
          RESOURCE_TYPES.IRON
      ];
  
      const villageFilters = {
          distance: function (target) {
              return !target.distance.between(
                  farmSettings[SETTINGS.MIN_DISTANCE],
                  farmSettings[SETTINGS.MAX_DISTANCE]
              );
          },
          ownPlayer: function (target) {
              return target.character_id === $player.getId();
          },
          included: function (target) {
              return target.character_id && !includedVillages.includes(target.id);
          },
          ignored: function (target) {
              return ignoredVillages.includes(target.id);
          },
          points: function (points) {
              return !points.between(
                  farmSettings[SETTINGS.MIN_POINTS],
                  farmSettings[SETTINGS.MAX_POINTS]
              );
          }
      };
  
      const targetFilters = [
          villageFilters.distance,
          villageFilters.ownPlayer,
          villageFilters.included,
          villageFilters.ignored
      ];
  
      const calcDistances = function (targets, origin) {
          return targets.map(function (target) {
              target.distance = math.actualDistance(origin, target);
              return target;
          });
      };
  
      const filterTargets = function (targets) {
          return targets.filter(function (target) {
              return targetFilters.every(function (fn) {
                  return !fn(target);
              });
          });
      };
  
      const sortTargets = function (targets) {
          return targets.sort(function (a, b) {
              return a.distance - b.distance;
          });
      };
  
      const arrayUnique = function (array) {
          return array.sort().filter(function (item, pos, ary) {
              return !pos || item != ary[pos - 1];
          });
      };
  
      const reloadTimers = function () {
          if (!running) {
              return;
          }
  
          if (stepDelayTimer) {
              stopTimers();
              activeFarmer.targetStep({
                  delay: true
              });
          } else if (cycleTimer) {
              stopTimers();
  
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_CYCLE_BEGIN);
  
              farmerIndex = 0;
              farmerStep();
          }
      };
  
      const updateIncludedVillage = function () {
          const groupsInclude = farmSettings[SETTINGS.GROUP_INCLUDE];
  
          includedVillages = [];
  
          groupsInclude.forEach(function (groupId) {
              const groupVillages = modelDataService.getGroupList().getGroupVillageIds(groupId);
              includedVillages = includedVillages.concat(groupVillages);
          });
  
          includedVillages = arrayUnique(includedVillages);
      };
  
      const updateIgnoredVillage = function () {
          const groupIgnored = farmSettings[SETTINGS.GROUP_IGNORE];
          ignoredVillages = modelDataService.getGroupList().getGroupVillageIds(groupIgnored);
      };
  
      const updateOnlyVillage = function () {
          const groupsOnly = farmSettings[SETTINGS.GROUP_ONLY];
  
          onlyVillages = [];
  
          groupsOnly.forEach(function (groupId) {
              let groupVillages = modelDataService.getGroupList().getGroupVillageIds(groupId);
              groupVillages = groupVillages.filter(function (villageId) {
                  return !!$player.getVillage(villageId);
              });
  
              onlyVillages = onlyVillages.concat(groupVillages);
          });
  
          onlyVillages = arrayUnique(onlyVillages);
      };
  
      const updateExceptionLogs = function () {
          const exceptionVillages = ignoredVillages.concat(includedVillages);
          let modified = false;
  
          exceptionVillages.forEach(function (villageId) {
              if (!hasOwn.call(exceptionLogs, villageId)) { 
                  exceptionLogs[villageId] = {
                      time: timeHelper.gameTime(),
                      report: false
                  };
                  modified = true;
              }
          });
  
          utils.each(exceptionLogs, function (time, villageId) {
              villageId = parseInt(villageId, 10);
              
              if (!exceptionVillages.includes(villageId)) {
                  delete exceptionLogs[villageId];
                  modified = true;
              }
          });
  
          if (modified) {
              Lockr.set(STORAGE_KEYS.EXCEPTION_LOGS, exceptionLogs);
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_EXCEPTION_LOGS_UPDATED);
          }
      };
  
      const updateGroupVillages = function () {
          updateIncludedVillage();
          updateIgnoredVillage();
          updateOnlyVillage();
          updateExceptionLogs();
  
          eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_EXCEPTION_VILLAGES_UPDATED);
      };
  
      const villageGroupLink = function (event, data) {
          const groupsInclude = farmSettings[SETTINGS.GROUP_INCLUDE];
          const groupIgnore = farmSettings[SETTINGS.GROUP_IGNORE];
          const groupsOnly = farmSettings[SETTINGS.GROUP_ONLY];
          const isOwnVillage = $player.getVillage(data.village_id);
          let farmerListUpdated = false;
  
          updateGroupVillages();
  
          if (groupIgnore === data.group_id) {
              if (isOwnVillage) {
                  removeFarmer(data.village_id);
                  farmerListUpdated = true;
              } else {
                  removeTarget(data.village_id);
  
                  addLog(LOG_TYPES.IGNORED_VILLAGE, {
                      villageId: data.village_id
                  });
                  addExceptionLog(data.village_id);
              }
          }
  
          if (groupsInclude.includes(data.group_id) && !isOwnVillage) {
              reloadTargets();
  
              addLog(LOG_TYPES.INCLUDED_VILLAGE, {
                  villageId: data.village_id
              });
              addExceptionLog(data.village_id);
          }
  
          if (groupsOnly.includes(data.group_id) && isOwnVillage) {
              const farmer = createFarmer(data.village_id);
              farmer.init().then(function () {
                  if (running) {
                      farmer.start();
                  }
              });
  
              farmerListUpdated = true;
          }
  
          if (farmerListUpdated) {
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_FARMER_VILLAGES_UPDATED);
          }
      };
  
      const villageGroupUnlink = function (event, data) {
          const groupsInclude = farmSettings[SETTINGS.GROUP_INCLUDE];
          const groupIgnore = farmSettings[SETTINGS.GROUP_IGNORE];
          const groupsOnly = farmSettings[SETTINGS.GROUP_ONLY];
          const isOwnVillage = $player.getVillage(data.village_id);
          let farmerListUpdated = false;
  
          updateGroupVillages();
  
          if (groupIgnore === data.group_id) {
              if (isOwnVillage) {
                  const farmer = createFarmer(data.village_id);
                  farmer.init().then(function () {
                      if (running) {
                          farmer.start();
                      }
                  });
  
                  farmerListUpdated = true;
              } else {
                  reloadTargets();
  
                  addLog(LOG_TYPES.IGNORED_VILLAGE_REMOVED, {
                      villageId: data.village_id
                  });
              }
          }
  
          if (groupsInclude.includes(data.group_id) && !isOwnVillage) {
              reloadTargets();
  
              addLog(LOG_TYPES.INCLUDED_VILLAGE_REMOVED, {
                  villageId: data.village_id
              });
          }
  
          if (groupsOnly.includes(data.group_id) && isOwnVillage) {
              removeFarmer(data.village_id);
              farmerListUpdated = true;
          }
  
          if (farmerListUpdated) {
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_FARMER_VILLAGES_UPDATED);
          }
      };
  
      const validGroups = function (_flag) {
          const gameGroups = modelDataService.getGroupList().getGroups();
          const groupIgnore = farmSettings[SETTINGS.GROUP_IGNORE];
  
          const groupsOnly = farmSettings[SETTINGS.GROUP_ONLY];
          const groupsInclude = farmSettings[SETTINGS.GROUP_INCLUDE];
          const validedGroupIgnore = hasOwn.call(gameGroups, groupIgnore) ? groupIgnore : settings.getDefault(SETTINGS.GROUP_IGNORE);
          const validedGroupsOnly = groupsOnly.filter(groupId => hasOwn.call(gameGroups, groupId));
          const validedGroupsInclude = groupsInclude.filter(groupId => hasOwn.call(gameGroups, groupId));
  
          settings.setAll({
              [SETTINGS.GROUP_IGNORE]: validedGroupIgnore,
              [SETTINGS.GROUP_ONLY]: validedGroupsOnly,
              [SETTINGS.GROUP_INCLUDE]: validedGroupsInclude
          }, _flag);
      };
  
      const removedGroupListener = function () {
          validGroups();
          updateGroupVillages();
  
          flushFarmers();
          reloadTargets();
          createFarmers();
      };
  
      const processPresets = function () {
          selectedPresets = [];
          const playerPresets = modelDataService.getPresetList().getPresets();
          const activePresets = farmSettings[SETTINGS.PRESETS];
  
          activePresets.forEach(function (presetId) {
              if (!hasOwn.call(playerPresets, presetId)) {
                  return;
              }
  
              const preset = playerPresets[presetId];
              preset.load = getPresetHaul(preset);
              preset.travelTime = armyService.calculateTravelTime(preset, {
                  barbarian: false,
                  officers: false
              });
  
              selectedPresets.push(preset);
          });
  
          selectedPresets = selectedPresets.sort(function (a, b) {
              return a.travelTime - b.travelTime || b.load - a.load;
          });
      };
  
      const ignoreVillage = function (villageId) {
          const groupIgnore = farmSettings[SETTINGS.GROUP_IGNORE];
  
          if (!groupIgnore) {
              return false;
          }
  
          socketService.emit(routeProvider.GROUPS_LINK_VILLAGE, {
              group_id: groupIgnore,
              village_id: villageId
          });
  
          return true;
      };
  
      const presetListener = function () {
          processPresets();
  
          if (!selectedPresets.length) {
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_STOP, {
                  reason: ERROR_TYPES.NO_PRESETS
              });
  
              if (running) {
                  farmOverflow.stop();
              }
          }
      };
  
      const reportListener = function (event, data) {
          if (!farmSettings[SETTINGS.IGNORE_ON_LOSS] || !farmSettings[SETTINGS.GROUP_IGNORE]) {
              return;
          }
  
          if (!running || data.type !== COMMAND_TYPES.TYPES.ATTACK) {
              return;
          }
  
          // 1 = nocasualties
          // 2 = casualties
          // 3 = defeat
          if (data.result !== 1 && isTarget(data.target_village_id)) {
              tempVillageReports[data.target_village_id] = {
                  haul: data.haul,
                  id: data.id,
                  result: data.result,
                  title: data.title
              };
  
              ignoreVillage(data.target_village_id);
          }
      };
  
      const commandSentListener = function (event, data) {
          if (!activeFarmer || !currentTarget) {
              return;
          }
  
          if (data.origin.id !== activeFarmer.getId()) {
              return;
          }
  
          if (data.target.id !== currentTarget.id) {
              return;
          }
  
          if (data.direction === 'forward' && data.type === COMMAND_TYPES.TYPES.ATTACK) {
              activeFarmer.commandSent(data);
          }
      };
  
      const commandErrorListener = function (event, data) {
          if (!activeFarmer || !sendingCommand || !currentTarget) {
              return;
          }
  
          if (data.cause === routeProvider.SEND_PRESET.type) {
              activeFarmer.commandError(data);
          }
      };
  
      const getPresetHaul = function (preset) {
          let haul = 0;
  
          utils.each(preset.units, function (unitAmount, unitName) {
              if (unitAmount) {
                  haul += unitsData[unitName].load * unitAmount;
              }
          });
  
          return haul;
      };
  
      const addExceptionLog = function (villageId) {
          exceptionLogs[villageId] = {
              time: timeHelper.gameTime(),
              report: tempVillageReports[villageId] || false
          };
  
          delete tempVillageReports[villageId];
  
          Lockr.set(STORAGE_KEYS.EXCEPTION_LOGS, exceptionLogs);
          eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_EXCEPTION_LOGS_UPDATED);
      };
  
      const addLog = function (type, data = {}) {
          if (typeof type !== 'string') {
              return false;
          }
  
          if (!angular.isObject(data)) {
              data = {};
          }
  
          data.time = timeHelper.gameTime();
          data.type = type;
  
          logs.unshift(data);
          trimAndSaveLogs();
  
          return true;
      };
  
      const trimAndSaveLogs = function () {
          const limit = farmSettings[SETTINGS.LOGS_LIMIT];
  
          if (logs.length > limit) {
              logs.splice(logs.length - limit, logs.length);
          }
  
          Lockr.set(STORAGE_KEYS.LOGS, logs);
          eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_LOGS_UPDATED);
      };
  
      const isTargetBusy = function (attacking, otherAttacking, allVillagesLoaded) {
          const multipleFarmers = farmSettings[SETTINGS.TARGET_MULTIPLE_FARMERS];
          const singleAttack = farmSettings[SETTINGS.TARGET_SINGLE_ATTACK];
  
          if (multipleFarmers && allVillagesLoaded) {
              if (singleAttack && attacking) {
                  return true;
              }
          } else if (singleAttack) {
              if (attacking || otherAttacking) {
                  return true;
              }
          } else if (otherAttacking) {
              return true;
          }
  
          return false;
      };
  
      const enableRequiredPresets = function (villageId, callback) {
          const villagePresets = modelDataService.getPresetList().getPresetsByVillageId(villageId);
          const missingPresets = [];
  
          selectedPresets.forEach(function (preset) {
              if (!hasOwn.call(villagePresets, preset.id)) {
                  missingPresets.push(preset.id);
              }
          });
  
          if (missingPresets.length) {
              // include already enabled presets because you can't only enable
              // missing ones, you need to emit all you want enabled.
              for (const id in villagePresets) {
                  if (hasOwn.call(villagePresets, id)) {
                      missingPresets.push(id);
                  }
              }
  
              socketService.emit(routeProvider.ASSIGN_PRESETS, {
                  village_id: villageId,
                  preset_ids: missingPresets
              }, callback);
  
              return;
          }
  
          callback();
      };
  
      const persistentRunningStart = function () {
          const cycleInterval = getCycleInterval();
          const attackInterval = getAttackInterval();
          const timeLimit = cycleInterval + (cycleInterval / 2) + attackInterval;
  
          persistentRunningTimer = setInterval(function () {
              const now = timeHelper.gameTime();
  
              if (now - persistentRunningLastCheck > timeLimit) {
                  farmOverflow.stop();
                  setTimeout(farmOverflow.start, 5000);
              }
          }, PERSISTENT_RUNNING_CHECK_INTERVAL);
      };
  
      const persistentRunningStop = function () {
          clearInterval(persistentRunningTimer);
      };
  
      const persistentRunningUpdate = function () {
          persistentRunningLastCheck = timeHelper.gameTime();
      };
  
      const stopTimers = function () {
          clearTimeout(cycleTimer);
          clearTimeout(stepDelayTimer);
          clearTimeout(commandExpireTimer);
  
          cycleTimer = null;
          stepDelayTimer = null;
          commandExpireTimer = null;
      };
  
      const getCycleInterval = function () {
          return Math.max(MINIMUM_FARMER_CYCLE_INTERVAL, farmSettings[SETTINGS.FARMER_CYCLE_INTERVAL] * 60 * 1000);
      };
  
      const getAttackInterval = function () {
          return Math.max(MINIMUM_ATTACK_INTERVAL, farmSettings[SETTINGS.ATTACK_INTERVAL] * 1000);
      };
  
      const Farmer = function (villageId) {
          this.villageId = villageId;
          this.village = $player.getVillage(villageId);
  
          if (!this.village) {
              throw new Error(`new Farmer -> Village ${villageId} doesn't exist.`);
          }
  
          this.index = 0;
          this.running = false;
          this.initialized = false;
          this.targets = false;
          this.onCycleEndFn = noop;
          this.status = STATUS.WAITING_CYCLE;
      };
  
      Farmer.prototype.init = function () {
          const loadPromises = [];
  
          if (!this.isInitialized()) {
              loadPromises.push(new Promise((resolve) => {
                  if (this.isInitialized()) {
                      return resolve();
                  }
  
                  villageService.ensureVillageDataLoaded(this.villageId, resolve);
              }));
  
              loadPromises.push(new Promise((resolve) => {
                  if (this.isInitialized()) {
                      return resolve();
                  }
  
                  this.loadTargets(() => {
                      eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_INSTANCE_READY, {
                          villageId: this.villageId
                      });
                      resolve();
                  });
              }));
          }
  
          return Promise.all(loadPromises).then(() => {
              this.initialized = true;
          });
      };
  
      Farmer.prototype.start = function () {
          persistentRunningUpdate();
  
          if (this.running) {
              return false;
          }
  
          if (!this.initialized) {
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_INSTANCE_ERROR_NOT_READY, {
                  villageId: this.villageId
              });
              return false;
          }
  
          if (!this.targets.length) {
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_INSTANCE_ERROR_NO_TARGETS, {
                  villageId: this.villageId
              });
              return false;
          }
  
          activeFarmer = this;
          this.running = true;
          eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_INSTANCE_START, {
              villageId: this.villageId
          });
  
          this.targetStep({
              delay: false
          });
  
          return true;
      };
  
      Farmer.prototype.stop = function (reason) {
          this.running = false;
  
          eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_INSTANCE_STOP, {
              villageId: this.villageId,
              reason: reason
          });
  
          if (reason === ERROR_TYPES.USER_STOP) {
              this.setStatus(STATUS.USER_STOP);
          }
  
          stopTimers();
  
          this.onCycleEndFn(reason);
          this.onCycleEndFn = noop;
      };
  
      Farmer.prototype.targetStep = async function (options = {}) {
          if (!this.running) {
              return false;
          }
  
          persistentRunningUpdate();
  
          const commandList = this.village.getCommandListModel();
          const villageCommands = commandList.getOutgoingCommands(true, true);
          let selectedPreset = false;
          let target;
          let checkedLocalCommands = false;
          let otherVillageAttacking;
          let thisVillageAttacking;
          let playerVillages;
  
          const delayStep = () => {
              return new Promise((resolve, reject) => {
                  if (options.delay) {
                      stepDelayTimer = setTimeout(() => {
                          stepDelayTimer = null;
  
                          if (!this.running) {
                              return reject(STATUS.USER_STOP);
                          }
  
                          resolve();
                      }, utils.randomSeconds(getAttackInterval()));
                  } else {
                      resolve();
                  }
              });
          };
  
          const checkCommandLimit = () => {
              return new Promise((resolve, reject) => {
                  const limit = VILLAGE_COMMAND_LIMIT - farmSettings[SETTINGS.PRESERVE_COMMAND_SLOTS];
  
                  if (villageCommands.length >= limit) {
                      reject(STATUS.COMMAND_LIMIT);
                  } else {
                      resolve();
                  }
              });
          };
  
          const checkStorage = () => {
              return new Promise((resolve, reject) => {
                  if (farmSettings[SETTINGS.IGNORE_FULL_STORAGE]) {
                      const resources = this.village.getResources();
                      const computed = resources.getComputed();
                      const maxStorage = resources.getMaxStorage();
                      const isFull = RESOURCES.every((type) => computed[type].currentStock === maxStorage);
  
                      if (isFull) {
                          return reject(STATUS.FULL_STORAGE);
                      }
                  }
  
                  resolve();
              });
          };
  
          const selectTarget = () => {
              return new Promise((resolve, reject) => {
                  if (!this.targets.length) {
                      return reject(STATUS.NO_TARGETS);
                  }
  
                  if (this.index > this.targets.length || !this.targets[this.index]) {
                      return reject(STATUS.TARGET_CYCLE_END);
                  }
  
                  target = this.targets[this.index];
  
                  resolve();
              });
          };
  
          const checkTarget = () => {
              return new Promise((resolve, reject) => {
                  $mapData.getTownAtAsync(target.x, target.y, (data) => {
                      if (villageFilters.points(data.points)) {
                          return reject(STATUS.NOT_ALLOWED_POINTS);
                      }
  
                      socketService.emit(routeProvider.GET_ATTACKING_FACTOR, {
                          target_id: target.id
                      }, (data) => {
                          if (!this.running) {
                              reject(STATUS.USER_STOP);
                          // abandoned village conquered by some noob.
                          } else if (target.character_id === null && data.owner_id !== null && !includedVillages.includes(target.id)) {
                              reject(STATUS.ABANDONED_CONQUERED);
                          } else if (target.attack_protection) {
                              reject(STATUS.PROTECTED_VILLAGE);
                          } else {
                              resolve();
                          }
                      });
                  });
              });
          };
  
          const checkPresets = () => {
              return new Promise((resolve, reject) => {
                  enableRequiredPresets(this.villageId, () => {
                      if (this.running) {
                          resolve();
                      } else {
                          reject(STATUS.USER_STOP);
                      }
                  });
              });
          };
  
          const selectPreset = () => {
              return new Promise((resolve, reject) => {
                  const villageUnits = this.village.getUnitInfo().getUnits();
                  const maxTravelTime = farmSettings[SETTINGS.MAX_TRAVEL_TIME] * 60;
                  const villagePosition = this.village.getPosition();
                  const targetDistance = math.actualDistance(villagePosition, target);
  
                  utils.each(selectedPresets, (preset) => {
                      const enoughUnits = !Object.entries(preset.units).some((unit) => {
                          const name = unit[0];
                          const amount = unit[1];
                          
                          return villageUnits[name].in_town < amount;
                      });
  
                      if (!enoughUnits) {
                          return;
                      }
  
                      const travelTime = armyService.calculateTravelTime(preset, {
                          barbarian: !target.character_id,
                          officers: false
                      });
  
                      if (maxTravelTime > travelTime * targetDistance) {
                          selectedPreset = preset;
                          resolve();
                      } else {
                          // why reject with TIME_LIMIT if there are more presets to check?
                          // because the preset list is sorted by travel time.
                          reject(STATUS.TIME_LIMIT);
                      }
  
                      return false;
                  });
  
                  if (!selectedPreset) {
                      reject(STATUS.NO_UNITS);
                  }
              });
          };
  
          const checkLocalCommands = () => {
              return new Promise((resolve, reject) => {
                  otherVillageAttacking = false;
                  playerVillages = $player.getVillageList();
                  
                  const allVillagesLoaded = playerVillages.every((anotherVillage) => anotherVillage.isInitialized(VILLAGE_CONFIG.READY_STATES.OWN_COMMANDS));
  
                  if (allVillagesLoaded) {
                      otherVillageAttacking = playerVillages.some((anotherVillage) => {
                          if (anotherVillage.getId() === this.villageId) {
                              return false;
                          }
  
                          const otherVillageCommands = anotherVillage.getCommandListModel().getOutgoingCommands(true, true);
  
                          return otherVillageCommands.some((command) => {
                              return command.targetVillageId === target.id && command.data.direction === 'forward';
                          });
                      });
                  }
  
                  thisVillageAttacking = villageCommands.some((command) => {
                      return command.data.target.id === target.id && command.data.direction === 'forward';
                  });
  
                  if (isTargetBusy(thisVillageAttacking, otherVillageAttacking, allVillagesLoaded)) {
                      return reject(STATUS.BUSY_TARGET);
                  }
  
                  if (allVillagesLoaded) {
                      checkedLocalCommands = true;
                  }
  
                  resolve();
              });
          };
  
          const minimumInterval = () => {
              return new Promise((resolve, reject) => {
                  if (!thisVillageAttacking && !otherVillageAttacking) {
                      return resolve();
                  }
  
                  const multipleAttacksInterval = farmSettings[SETTINGS.MULTIPLE_ATTACKS_INTERVAL] * 60;
  
                  if (!multipleAttacksInterval) {
                      return resolve();
                  }
  
                  // if TARGET_SINGLE_ATTACK is enabled, and TARGET_MULTIPLE_FARMERS is disabled
                  // there's no reason the check, since the target is allowed to receive multiple
                  // attacks simultaneously.
                  if (farmSettings[SETTINGS.TARGET_SINGLE_ATTACK] && !farmSettings[SETTINGS.TARGET_MULTIPLE_FARMERS]) {
                      return resolve();
                  }
  
                  const now = Math.round(timeHelper.gameTime() / 1000);
                  const villages = farmSettings[SETTINGS.TARGET_MULTIPLE_FARMERS] ? playerVillages : [this.village];
                  const position = this.village.getPosition();
                  const distance = math.actualDistance(position, target);
                  const singleFieldtravelTime = armyService.calculateTravelTime(selectedPreset, {
                      barbarian: !target.character_id,
                      officers: true,
                      effects: true
                  });
                  const commandTravelTime = armyService.getTravelTimeForDistance(selectedPreset, singleFieldtravelTime, distance, COMMAND_TYPES.TYPES.ATTACK);
  
                  const busyTarget = villages.some((village) => {
                      const commands = village.getCommandListModel().getOutgoingCommands(true, true);
                      const targetCommands = commands.filter((command) => command.targetVillageId === target.id && command.data.direction === 'forward');
  
                      if (targetCommands.length) {
                          return targetCommands.some((command) => {
                              return Math.abs((now + commandTravelTime) - command.time_completed) < multipleAttacksInterval;
                          });
                      }
  
                      return false;
                  });
  
                  if (busyTarget) {
                      return reject(STATUS.BUSY_TARGET);
                  }
  
                  resolve();
              });
          };
  
          const checkLoadedCommands = () => {
              return new Promise((resolve, reject) => {
                  if (checkedLocalCommands) {
                      return resolve();
                  }
  
                  socketService.emit(routeProvider.MAP_GET_VILLAGE_DETAILS, {
                      my_village_id: this.villageId,
                      village_id: target.id,
                      num_reports: 0
                  }, (data) => {
                      if (!this.running) {
                          return reject(STATUS.USER_STOP);
                      }
  
                      const targetCommands = data.commands.own.filter((command) => command.type === COMMAND_TYPES.TYPES.ATTACK && command.direction === 'forward');
                      const otherAttacking = targetCommands.some((command) => command.start_village_id !== this.villageId);
                      const attacking = targetCommands.some((command) => command.start_village_id === this.villageId);
  
                      if (isTargetBusy(attacking, otherAttacking, true)) {
                          return reject(STATUS.BUSY_TARGET);
                      }
  
                      resolve();
                  });
              });
          };
  
          const prepareAttack = () => {
              if (!this.running) {
                  return false;
              }
  
              this.setStatus(STATUS.ATTACKING);
  
              sendingCommand = true;
              currentTarget = target;
              this.index++;
  
              socketService.emit(routeProvider.SEND_PRESET, {
                  start_village: this.villageId,
                  target_village: target.id,
                  army_preset_id: selectedPreset.id,
                  type: COMMAND_TYPES.TYPES.ATTACK
              });
          };
  
          const stepStatus = (status) => {
              stopTimers();
  
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_INSTANCE_STEP_STATUS, {
                  villageId: this.villageId,
                  error: status
              });
  
              switch (status) {
                  case STATUS.TIME_LIMIT:
                  case STATUS.BUSY_TARGET:
                  case STATUS.ABANDONED_CONQUERED:
                  case STATUS.PROTECTED_VILLAGE: {
                      this.index++;
                      this.setStatus(status);
                      this.targetStep(options);
                      break;
                  }
                  case STATUS.USER_STOP: {
                      this.setStatus(status);
                      break;
                  }
                  case STATUS.NOT_ALLOWED_POINTS: {
                      this.index++;
                      this.setStatus(status);
                      removeTarget(target.id);
                      this.targetStep(options);
                      break;
                  }
                  case STATUS.NO_UNITS:
                  case STATUS.NO_TARGETS:
                  case STATUS.FULL_STORAGE:
                  case STATUS.COMMAND_LIMIT: {
                      this.index++;
                      this.setStatus(status);
                      this.stop(status);
                      break;
                  }
                  case STATUS.TARGET_CYCLE_END: {
                      this.index = 0;
                      this.setStatus(status);
                      this.stop(status);
                      break;
                  }
                  case STATUS.EXPIRED_STEP: {
                      this.setStatus(status);
                      this.targetStep();
                      break;
                  }
                  default: {
                      // eslint-disable-next-line no-console
                      console.error('Unknown status:', status);
                      this.index++;
                      this.setStatus(STATUS.UNKNOWN);
                      this.stop(STATUS.UNKNOWN);
                      break;
                  }
              }
          };
  
          const attackPromise = new Promise((resolve, reject) => {
              delayStep()
                  .then(checkCommandLimit)
                  .then(checkStorage)
                  .then(selectTarget)
                  .then(checkTarget)
                  .then(checkPresets)
                  .then(selectPreset)
                  .then(checkLocalCommands)
                  .then(minimumInterval)
                  .then(checkLoadedCommands)
                  .then(resolve)
                  .catch(reject);
          });
  
          const expirePromise = new Promise((resolve, reject) => {
              commandExpireTimer = setTimeout(() => {
                  if (this.running) {
                      reject(STATUS.EXPIRED_STEP);
                  }
              }, STEP_EXPIRE_TIME);
          });
  
          Promise.race([attackPromise, expirePromise])
              .then(prepareAttack)
              .catch(stepStatus);
      };
  
      Farmer.prototype.setStatus = function (newStatus) {
          this.status = newStatus;
      };
  
      Farmer.prototype.getStatus = function () {
          return this.status || STATUS.UNKNOWN;
      };
  
      Farmer.prototype.commandSent = function (data) {
          sendingCommand = false;
          currentTarget = false;
  
          stopTimers();
  
          addLog(LOG_TYPES.ATTACKED_VILLAGE, {
              targetId: data.target.id
          });
  
          this.targetStep({
              delay: true
          });
      };
  
      Farmer.prototype.commandError = function () {
          sendingCommand = false;
          currentTarget = false;
  
          this.stop(STATUS.COMMAND_ERROR);
      };
  
      Farmer.prototype.onCycleEnd = function (handler) {
          this.onCycleEndFn = handler;
      };
  
      Farmer.prototype.loadTargets = function (callback) {
          const pos = this.village.getPosition();
  
          twoMapData.load((loadedTargets) => {
              this.targets = calcDistances(loadedTargets, pos);
              this.targets = filterTargets(this.targets, pos);
              this.targets = sortTargets(this.targets);
              this.targets = this.targets.slice(0, farmSettings[SETTINGS.TARGET_LIMIT_PER_VILLAGE]);
  
              if (typeof callback === 'function') {
                  callback(this.targets);
              }
          });
      };
  
      Farmer.prototype.getTargets = function () {
          return this.targets;
      };
  
      Farmer.prototype.getIndex = function () {
          return this.index;
      };
  
      Farmer.prototype.getVillage = function () {
          return this.village;
      };
  
      Farmer.prototype.isRunning = function () {
          return this.running;
      };
  
      Farmer.prototype.isInitialized = function () {
          return this.initialized;
      };
  
      Farmer.prototype.removeTarget = function (targetId) {
          if (typeof targetId !== 'number' || !this.targets) {
              return false;
          }
  
          this.targets = this.targets.filter(function (target) {
              return target.id !== targetId;
          });
  
          return true;
      };
  
      Farmer.prototype.getId = function () {
          return this.villageId;
      };
  
      const createFarmer = function (villageId) {
          const groupsOnly = farmSettings[SETTINGS.GROUP_ONLY];
  
          villageId = parseInt(villageId, 10);
  
          if (groupsOnly.length && !onlyVillages.includes(villageId)) {
              return false;
          }
  
          if (ignoredVillages.includes(villageId)) {
              return false;
          }
  
          let farmer = farmOverflow.getFarmer(villageId);
  
          if (!farmer) {
              farmer = new Farmer(villageId);
              farmers.push(farmer);
          }
  
          return farmer;
      };
  
      const createFarmers = function () {
          utils.each($player.getVillages(), function (village, villageId) {
              createFarmer(villageId);
          });
  
          eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_FARMER_VILLAGES_UPDATED);
      };
  
      /**
       * Clean farmer instances by removing villages based on
       * groups-only, only-villages and ignore-villages group filters.
       */
      const flushFarmers = function () {
          const groupsOnly = farmSettings[SETTINGS.GROUP_ONLY];
          const removeIds = [];
  
          farmers.forEach(function (farmer) {
              const villageId = farmer.getId();
  
              if (groupsOnly.length && !onlyVillages.includes(villageId)) {
                  removeIds.push(villageId);
              } else if (ignoredVillages.includes(villageId)) {
                  removeIds.push(villageId);
              }
          });
  
          if (removeIds.length) {
              removeIds.forEach(function (removeId) {
                  removeFarmer(removeId);
              });
  
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_FARMER_VILLAGES_UPDATED);
          }
      };
  
      const removeFarmer = function (farmerId) {
          for (let i = 0; i < farmers.length; i++) {
              if (farmers[i].getId() === farmerId) {
                  farmers[i].stop(ERROR_TYPES.KILL_FARMER);
                  farmers.splice(i, i + 1);
  
                  return true;
              }
          }
  
          return false;
      };
  
      const farmerStep = function (status) {
          persistentRunningUpdate();
  
          if (!farmers.length) {
              activeFarmer = false;
          } else if (farmerIndex >= farmers.length) {
              farmerIndex = 0;
              activeFarmer = false;
              nextCycleDate = timeHelper.gameTime() + getCycleInterval();
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_CYCLE_END);
          } else {
              activeFarmer = farmers[farmerIndex];
          }
  
          if (activeFarmer) {
              activeFarmer.onCycleEnd(function (reason) {
                  if (reason !== ERROR_TYPES.USER_STOP) {
                      farmerIndex++;
                      farmerStep();
                  }
              });
  
              if (status === CYCLE_BEGIN) {
                  nextCycleDate = null;
                  eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_CYCLE_BEGIN);
              }
  
              activeFarmer.start();
          } else {
              cycleTimer = setTimeout(function () {
                  cycleTimer = null;
                  farmerIndex = 0;
                  nextCycleDate = null;
                  eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_CYCLE_BEGIN);
                  farmerStep();
              }, getCycleInterval());
          }
      };
  
      const isTarget = function (targetId) {
          for (let i = 0; i < farmers.length; i++) {
              const farmer = farmers[i];
              const targets = farmer.getTargets();
  
              for (let j = 0; j < targets.length; j++) {
                  const target = targets[j];
  
                  if (target.id === targetId) {
                      return true;
                  }
              }
          }
  
          return false;
      };
  
      const removeTarget = function (targetId) {
          farmers.forEach(function (farmer) {
              farmer.removeTarget(targetId);
          });
      };
  
      const reloadTargets = function () {
          twoMapData.load(function () {
              farmers.forEach(function (farmer) {
                  farmer.loadTargets();
              });
          }, true);
      };
  
      const farmOverflow = {};
  
      farmOverflow.init = function () {
          initialized = true;
          logs = Lockr.get(STORAGE_KEYS.LOGS, []);
          exceptionLogs = Lockr.get(STORAGE_KEYS.EXCEPTION_LOGS, {});
          $player = modelDataService.getSelectedCharacter();
          unitsData = modelDataService.getGameData().getUnitsObject();
  
          settings = new Settings({
              settingsMap: SETTINGS_MAP,
              storageKey: STORAGE_KEYS.SETTINGS
          });
  
          settings.onChange(function (changes, updates, _flag) {
              farmSettings = settings.getAll();
  
              if (_flag === IGNORE_UPDATES) {
                  return;
              }
  
              if (updates[UPDATES.PRESET]) {
                  processPresets();
              }
  
              if (updates[UPDATES.GROUPS]) {
                  updateGroupVillages();
              }
  
              if (updates[UPDATES.TARGETS]) {
                  reloadTargets();
              }
  
              if (updates[UPDATES.VILLAGES]) {
                  flushFarmers();
                  createFarmers();
              }
  
              if (updates[UPDATES.LOGS]) {
                  trimAndSaveLogs();
              }
  
              if (updates[UPDATES.INTERVAL_TIMERS]) {
                  reloadTimers();
              }
          });
  
          farmSettings = settings.getAll();
  
          validGroups(IGNORE_UPDATES);
          updateGroupVillages();
          createFarmers();
  
          ready(function () {
              processPresets();
          }, 'presets');
  
          ready(function () {
              farmers.forEach(function (farmer) {
                  farmer.loadTargets();
              });
          }, 'minimap_data');
  
          $rootScope.$on(eventTypeProvider.ARMY_PRESET_UPDATE, presetListener);
          $rootScope.$on(eventTypeProvider.ARMY_PRESET_DELETED, presetListener);
          $rootScope.$on(eventTypeProvider.GROUPS_VILLAGE_LINKED, villageGroupLink);
          $rootScope.$on(eventTypeProvider.GROUPS_VILLAGE_UNLINKED, villageGroupUnlink);
          $rootScope.$on(eventTypeProvider.GROUPS_DESTROYED, removedGroupListener);
          $rootScope.$on(eventTypeProvider.COMMAND_SENT, commandSentListener);
          klikam();
          $rootScope.$on(eventTypeProvider.MESSAGE_ERROR, commandErrorListener);
          $rootScope.$on(eventTypeProvider.REPORT_NEW, reportListener);
      };
  
      farmOverflow.start = function () {
          if (running) {
              return false;
          }
  
          if (!selectedPresets.length) {
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_STOP, {
                  reason: ERROR_TYPES.NO_PRESETS
              });
  
              return false;
          }
  
          running = true;
  
          const readyFarmers = [];
  
          farmers.forEach(function (farmer) {
              readyFarmers.push(new Promise(function (resolve) {
                  farmer.init().then(resolve);
              }));
          });
  
          if (!readyFarmers.length) {
              running = false;
              return false;
          }
  
          Promise.all(readyFarmers).then(function () {
              farmerStep(CYCLE_BEGIN);
          });
  
          persistentRunningUpdate();
          persistentRunningStart();
  
          eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_START);
  
          addLog(LOG_TYPES.FARM_START);
      };
  
      farmOverflow.stop = function (reason = STATUS.USER_STOP) {
          if (activeFarmer) {
              activeFarmer.stop(reason);
              
              if (reason !== STATUS.USER_STOP) {
                  nextCycleDate = timeHelper.gameTime() + getCycleInterval();
              }
  
              eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_CYCLE_END, reason);
          } else {
              nextCycleDate = null;
          }
  
          running = false;
  
          stopTimers();
  
          eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_STOP, {
              reason: reason
          });
  
          persistentRunningStop();
  
          if (reason === STATUS.USER_STOP) {
              addLog(LOG_TYPES.FARM_STOP);
          }
      };
  
      farmOverflow.getFarmer = function (farmerId) {
          return farmers.find(function (farmer) {
              return farmer.getId() === farmerId;
          });
      };
  
      farmOverflow.getFarmers = function () {
          return farmers;
      };
  
      farmOverflow.getSettings = function () {
          return settings;
      };
  
      farmOverflow.getExceptionVillages = function () {
          return {
              included: includedVillages,
              ignored: ignoredVillages
          };
      };
  
      farmOverflow.getExceptionLogs = function () {
          return exceptionLogs;
      };
  
      farmOverflow.isInitialized = function () {
          return initialized;
      };
  
      farmOverflow.isRunning = function () {
          return running;
      };
  
      farmOverflow.getLogs = function () {
          return logs;
      };
  
      farmOverflow.clearLogs = function () {
          logs = [];
          Lockr.set(STORAGE_KEYS.LOGS, logs);
          eventQueue.trigger(eventTypeProvider.FARM_OVERFLOW_LOGS_UPDATED);
  
          return logs;
      };
  
      farmOverflow.getNextCycleDate = function () {
          return nextCycleDate;
      };
  
      farmOverflow.getCycleInterval = getCycleInterval;
  
      return farmOverflow;
  });
  
  define('two/farmOverflow/errorTypes', [], function () {
      return {
          TIME_LIMIT: 'time_limit',
          COMMAND_LIMIT: 'command_limit',
          FULL_STORAGE: 'full_storage',
          NO_UNITS: 'no_units',
          PRESET_FIRST: 'preset_first',
          NO_SELECTED_VILLAGE: 'no_selected_village'
      }
  })
  
  define('two/farmOverflow/events', [], function () {
      angular.extend(eventTypeProvider, {
          FARM_NO_PRESET: 'farmoverflow_no_preset',
          FARM_NO_VILLAGE_SELECTED: 'farmoverflow_no_village_selected',
          FARM_NO_TARGETS: 'farmoverflow_no_targets',
          FARM_NO_UNITS: 'farmoverflow_no_units',
          FARM_NO_UNITS_NO_COMMANDS: 'farmoverflow_no_units_no_commands',
          FARM_COMMAND_LIMIT_SINGLE: 'farmoverflow_command_limit_single',
          FARM_COMMAND_LIMIT_MULTI: 'farmoverflow_command_limit_multi',
          FARM_FULL_STORAGE: 'farmoverflow_full_storage',
          FARM_SEND_COMMAND: 'farmoverflow_send_command',
          FARM_SEND_COMMAND_ERROR: 'farmoverflow_send_command_error',
          FARM_VILLAGES_UPDATE: 'farmoverflow_villages_update',
          FARM_PRESETS_LOADED: 'farmoverflow_presets_loaded',
          FARM_PRIORITY_TARGET_ADDED: 'farmoverflow_priority_target_added',
          FARM_REMOTE_COMMAND: 'farmoverflow_remote_command',
          FARM_PRESETS_CHANGE: 'farmoverflow_presets_change',
          FARM_GROUPS_CHANGED: 'farmoverflow_groups_changed',
          FARM_LOGS_UPDATED: 'farmoverflow_logs_updated',
          FARM_LOGS_RESETED: 'farmoverflow_logs_reseted',
          FARM_IGNORED_VILLAGE: 'farmoverflow_ignored_village',
          FARM_LOADING_TARGETS_START: 'farmoverflow_loading_targets_start',
          FARM_LOADING_TARGETS_END: 'farmoverflow_loading_targets_end',
          FARM_STATUS_CHANGE: 'farmoverflow_status_change',
          FARM_START: 'farmoverflow_start',
          FARM_PAUSE: 'farmoverflow_pause',
          FARM_RESET_LOGS: 'farmoverflow_reset_logs',
          FARM_SETTINGS_CHANGE: 'farmoverflow_settings_change',
          FARM_IGNORED_TARGET: 'farmoverflow_ignored_target',
          FARM_NEXT_VILLAGE: 'farmoverflow_next_village',
          FARM_NO_VILLAGES: 'farmoverflow_no_villages',
          FARM_STEP_CYCLE_RESTART: 'farmoverflow_step_cycle_restart',
          FARM_STEP_CYCLE_START: 'farmoverflow_step_cycle_start',
          FARM_STEP_CYCLE_END: 'farmoverflow_step_cycle_end',
          FARM_STEP_CYCLE_NEXT: 'farmoverflow_step_cycle_next',
          FARM_STEP_CYCLE_NEXT_NO_VILLAGES: 'farmoverflow_step_cycle_next_no_villages',
          FARM_STEP_CYCLE_END_NO_VILLAGES: 'farmoverflow_step_cycle_end_no_villages',
          FARM_ERROR: 'farmoverflow_error'
      })
  })
  
  define('two/farmOverflow/ui', [
      'two/ui',
      'two/ui/button',
      'two/farmOverflow',
      'two/farmOverflow/errorTypes',
      'two/farmOverflow/logTypes',
      'two/farmOverflow/settings',
      'two/Settings',
      'two/utils',
      'two/EventScope',
      'queues/EventQueue',
      'struct/MapData',
      'helper/time',
  ], function (
      interfaceOverflow,
      FrontButton,
      farmOverflow,
      ERROR_TYPES,
      LOG_TYPES,
      SETTINGS,
      Settings,
      utils,
      EventScope,
      eventQueue,
      mapData,
      timeHelper
  ) {
      var eventScope
      var $scope
      var textObject = 'farm_overflow'
      var textObjectCommon = 'common'
      var SELECT_SETTINGS = [
          SETTINGS.PRESETS,
          SETTINGS.GROUP_IGNORE,
          SETTINGS.GROUP_INCLUDE,
          SETTINGS.GROUP_ONLY
      ]
      var TAB_TYPES = {
          SETTINGS: 'settings',
          LOGS: 'logs'
      }
      var presetList = modelDataService.getPresetList()
      var groupList = modelDataService.getGroupList()
      var settings
  
      var selectTab = function (tabType) {
          $scope.selectedTab = tabType
      }
  
      var clearLogs = function () {
          $scope.logs = []
          $scope.visibleLogs = []
          farmOverflow.clearLogs()
      }
  
      var saveSettings = function () {
          settings.setSettings(settings.decodeSettings($scope.settings))
          utils.emitNotif('success', $filter('i18n')('settings_saved', $rootScope.loc.ale, textObject))
      }
  
      var switchFarm = function () {
          farmOverflow.switchState(true)
      }
  
      var loadVillagesLabel = function () {
          var load = function (data) {
              if ($scope.villagesLabel[data.coords]) {
                  return false
              }
  
              var coords = data.coords.split('|')
              var x = parseInt(coords[0], 10)
              var y = parseInt(coords[1], 10)
              
              mapData.getTownAtAsync(x, y, function (village) {
                  $scope.villagesLabel[data.coords] = `${village.name} (${data.coords})`
              })
          }
  
          $scope.logs.forEach(function (log) {
              if (log.origin) {
                  load(log.origin)
              }
  
              if (log.target) {
                  load(log.target)
              }
  
              if (log.village) {
                  load(log.village)
              }
          })
      }
  
      var updateVisibleLogs = function () {
          var offset = $scope.pagination.offset
          var limit = $scope.pagination.limit
  
          $scope.visibleLogs = $scope.logs.slice(offset, offset + limit)
      }
  
      var eventHandlers = {
          updatePresets: function () {
              $scope.presets = Settings.encodeList(presetList.getPresets(), {
                  disabled: false,
                  type: 'presets'
              })
          },
          updateGroups: function () {
              $scope.groups = Settings.encodeList(groupList.getGroups(), {
                  disabled: false,
                  type: 'groups'
              })
  
              $scope.groupsWithDisabled = Settings.encodeList(groupList.getGroups(), {
                  disabled: true,
                  type: 'groups'
              })
          },
          start: function (event, _manual) {
              $scope.running = true
  
              if (_manual) {
                  utils.emitNotif('success', $filter('i18n')('farm_started', $rootScope.loc.ale, textObject))
              }
          },
          pause: function (event, _manual) {
              $scope.running = false
  
              if (_manual) {
                  utils.emitNotif('success', $filter('i18n')('farm_paused', $rootScope.loc.ale, textObject))
              }
          },
          updateSelectedVillage: function () {
              $scope.selectedVillage = farmOverflow.getSelectedVillage()
          },
          updateLastAttack: function () {
              $scope.lastAttack = farmOverflow.getLastAttack()
          },
          updateCurrentStatus: function (event, status) {
              $scope.currentStatus = status
          },
          updateLogs: function () {
              $scope.logs = angular.copy(farmOverflow.getLogs())
  
              loadVillagesLabel()
              updateVisibleLogs()
          },
          resetLogsHandler: function () {
              utils.emitNotif('success', $filter('i18n')('reseted_logs', $rootScope.loc.ale, textObject))
          },
          stepCycleEndHandler: function () {
              if (settings.getSetting(SETTINGS.STEP_CYCLE_NOTIFS)) {
                  utils.emitNotif('error', $filter('i18n')('step_cycle_end', $rootScope.loc.ale, textObject))
              }
          },
          stepCycleEndNoVillagesHandler: function () {
              utils.emitNotif('error', $filter('i18n')('step_cycle_end_no_villages', $rootScope.loc.ale, textObject))
          },
          stepCycleNextHandler: function () {
              if (settings.getSetting(SETTINGS.STEP_CYCLE_NOTIFS)) {
                  var next = timeHelper.gameTime() + (settings.getSetting(SETTINGS.STEP_CYCLE_INTERVAL) * 60)
  
                  utils.emitNotif('success', $filter('i18n')('step_cycle_next', $rootScope.loc.ale, textObject, utils.formatDate(next)))
              }
          },
          errorHandler: function (event, args) {
              var error = args[0]
              var manual = args[1]
  
              if (!manual) {
                  return false
              }
  
              switch (error) {
              case ERROR_TYPES.PRESET_FIRST:
                  utils.emitNotif('error', $filter('i18n')('preset_first', $rootScope.loc.ale, textObject))
                  break
              case ERROR_TYPES.NO_SELECTED_VILLAGE:
                  utils.emitNotif('error', $filter('i18n')('no_selected_village', $rootScope.loc.ale, textObject))
                  break
              }
          }
      }
  
      var init = function () {
          settings = farmOverflow.getSettings()
  
          var opener = new FrontButton('Farmer', {
              classHover: false,
              classBlur: false,
              onClick: buildWindow
          })
  
          eventQueue.register(eventTypeProvider.FARM_START, function () {
              opener.$elem.classList.remove('btn-green')
              opener.$elem.classList.add('btn-red')
          })
  
          eventQueue.register(eventTypeProvider.FARM_PAUSE, function () {
              opener.$elem.classList.remove('btn-red')
              opener.$elem.classList.add('btn-green')
          })
  
          interfaceOverflow.addTemplate('twoverflow_farm_window', `<div id="two-farmoverflow" class="win-content two-window"><header class="win-head"><h2>FarmOverflow</h2><ul class="list-btn"><li><a href="#" class="size-34x34 btn-red icon-26x26-close" ng-click="closeWindow()"></a></li></ul></header><div class="win-main" scrollbar=""><div class="tabs tabs-bg"><div class="tabs-two-col"><div class="tab" ng-click="selectTab(TAB_TYPES.SETTINGS)" ng-class="{'tab-active': selectedTab == TAB_TYPES.SETTINGS}"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.SETTINGS}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.SETTINGS}">{{ TAB_TYPES.SETTINGS | i18n:loc.ale:textObjectCommon }}</a></div></div></div><div class="tab" ng-click="selectTab(TAB_TYPES.LOGS)" ng-class="{'tab-active': selectedTab == TAB_TYPES.LOGS}"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.LOGS}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.LOGS}">{{ TAB_TYPES.LOGS | i18n:loc.ale:textObjectCommon }}</a></div></div></div></div></div><div class="box-paper footer"><div class="scroll-wrap"><div class="settings" ng-show="selectedTab === TAB_TYPES.SETTINGS"><table class="tbl-border-light tbl-content tbl-medium-height"><colgroup><col><col width="200px"></colgroup><tr><th colspan="2">{{ 'groups_presets' | i18n:loc.ale:textObject }}</th></tr><tr><td><span class="ff-cell-fix">{{ 'presets' | i18n:loc.ale:textObject }}</span></td><td><div select="" list="presets" selected="settings[SETTINGS.PRESETS]" drop-down="true"></div></td></tr><tr><td><span class="ff-cell-fix">{{ 'group_ignored' | i18n:loc.ale:textObject }}</span></td><td><div select="" list="groupsWithDisabled" selected="settings[SETTINGS.GROUP_IGNORE]" drop-down="true"></div></td></tr><tr><td><span class="ff-cell-fix">{{ 'group_include' | i18n:loc.ale:textObject }}</span></td><td><div select="" list="groups" selected="settings[SETTINGS.GROUP_INCLUDE]" drop-down="true"></div></td></tr><tr><td><span class="ff-cell-fix">{{ 'group_only' | i18n:loc.ale:textObject }}</span></td><td><div select="" list="groups" selected="settings[SETTINGS.GROUP_ONLY]" drop-down="true"></div></td></tr></table><table class="tbl-border-light tbl-content tbl-medium-height"><colgroup><col><col width="200px"><col width="60px"></colgroup><tr><th colspan="3">{{ 'misc' | i18n:loc.ale:textObject }}</th></tr><tr><td><span class="ff-cell-fix">{{ 'random_base' | i18n:loc.ale:textObject }}</span></td><td><div range-slider="" min="0" max="30" value="settings[SETTINGS.RANDOM_BASE]" enabled="true"></div></td><td class="cell-bottom"><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.RANDOM_BASE]"></td></tr><tr><td><span class="ff-cell-fix">{{ 'commands_per_village' | i18n:loc.ale:textObject }}</span></td><td><div range-slider="" min="0" max="50" value="settings[SETTINGS.COMMANDS_PER_VILLAGE]" enabled="true"></div></td><td class="cell-bottom"><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.COMMANDS_PER_VILLAGE]"></td></tr><tr><td colspan="2"><span class="ff-cell-fix">{{ 'priority_targets' | i18n:loc.ale:textObject }}</span></td><td><div switch-slider="" enabled="true" border="true" value="settings[SETTINGS.PRIORITY_TARGETS]" vertical="false" size="'56x28'"></div></td></tr><tr><td colspan="2"><span class="ff-cell-fix">{{ 'ignore_on_loss' | i18n:loc.ale:textObject }}</span></td><td><div switch-slider="" enabled="settings[SETTINGS.GROUP_IGNORE].value" border="true" value="settings[SETTINGS.IGNORE_ON_LOSS]" vertical="false" size="'56x28'"></div></td></tr><tr><td colspan="2"><span class="ff-cell-fix">{{ 'ignore_full_storage' | i18n:loc.ale:textObject }}</span></td><td><div switch-slider="" enabled="true" border="true" value="settings[SETTINGS.IGNORE_FULL_STORAGE]" vertical="false" size="'56x28'"></div></td></tr></table><table class="tbl-border-light tbl-content tbl-medium-height"><colgroup><col><col width="200px"><col width="60px"></colgroup><tr><th colspan="3">{{ 'step_cycle_header' | i18n:loc.ale:textObject }}</th></tr><tr><td colspan="2"><span class="ff-cell-fix">{{ 'step_cycle' | i18n:loc.ale:textObject }}</span></td><td><div switch-slider="" enabled="true" border="true" value="settings[SETTINGS.STEP_CYCLE]" vertical="false" size="'56x28'"></div></td></tr><tr><td colspan="2"><span class="ff-cell-fix">{{ 'step_cycle_notifs' | i18n:loc.ale:textObject }}</span></td><td><div switch-slider="" enabled="settings[SETTINGS.STEP_CYCLE]" border="true" value="settings[SETTINGS.STEP_CYCLE_NOTIFS]" vertical="false" size="'56x28'"></div></td></tr><tr><td><span class="ff-cell-fix">{{ 'step_cycle_interval' | i18n:loc.ale:textObject }}</span></td><td><div range-slider="" min="0" max="120" value="settings[SETTINGS.STEP_CYCLE_INTERVAL]" enabled="settings[SETTINGS.STEP_CYCLE]"></div></td><td class="cell-bottom"><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.STEP_CYCLE_INTERVAL]" ng-disabled="!settings[SETTINGS.STEP_CYCLE]"></td></tr></table><table class="tbl-border-light tbl-content tbl-medium-height"><colgroup><col><col width="200px"><col width="60px"></colgroup><tr><th colspan="3">{{ 'target_filters' | i18n:loc.ale:textObject }}</th></tr><tr><td><span class="ff-cell-fix">{{ 'min_distance' | i18n:loc.ale:textObject }}</span></td><td><div range-slider="" min="0" max="50" value="settings[SETTINGS.MIN_DISTANCE]" enabled="true"></div></td><td class="cell-bottom"><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.MIN_DISTANCE]"></td></tr><tr><td><span class="ff-cell-fix">{{ 'max_distance' | i18n:loc.ale:textObject }}</span></td><td><div range-slider="" min="0" max="50" value="settings[SETTINGS.MAX_DISTANCE]" enabled="true"></div></td><td class="cell-bottom"><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.MAX_DISTANCE]"></td></tr><tr><td><span class="ff-cell-fix">{{ 'min_points' | i18n:loc.ale:textObject }}</span></td><td><div range-slider="" min="0" max="13000" value="settings[SETTINGS.MIN_POINTS]" enabled="true"></div></td><td class="cell-bottom"><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.MIN_POINTS]"></td></tr><tr><td><span class="ff-cell-fix">{{ 'max_points' | i18n:loc.ale:textObject }}</span></td><td><div range-slider="" min="0" max="13000" value="settings[SETTINGS.MAX_POINTS]" enabled="true"></div></td><td class="cell-bottom"><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.MAX_POINTS]"></td></tr><tr><td><span class="ff-cell-fix">{{ 'max_travel_time' | i18n:loc.ale:textObject }}</span></td><td><div range-slider="" min="0" max="180" value="settings[SETTINGS.MAX_TRAVEL_TIME]" enabled="true"></div></td><td class="cell-bottom"><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.MAX_TRAVEL_TIME]"></td></tr></table><table class="tbl-border-light tbl-content tbl-medium-height"><colgroup><col><col width="200px"><col width="60px"></colgroup><tr><th colspan="3">{{ TAB_TYPES.LOGS | i18n:loc.ale:textObjectCommon }}</th></tr><tr><td><span class="ff-cell-fix">{{ 'logs_limit' | i18n:loc.ale:textObject }}</span></td><td><div range-slider="" min="0" max="1000" value="settings[SETTINGS.LOGS_LIMIT]" enabled="true"></div></td><td class="cell-bottom"><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.LOGS_LIMIT]"></td></tr><tr><td colspan="2"><span class="ff-cell-fix">{{ 'event_attack' | i18n:loc.ale:textObject }}</span></td><td><div switch-slider="" enabled="true" border="true" value="settings[SETTINGS.EVENT_ATTACK]" vertical="false" size="'56x28'"></div></td></tr><tr><td colspan="2"><span class="ff-cell-fix">{{ 'event_village_change' | i18n:loc.ale:textObject }}</span></td><td><div switch-slider="" enabled="true" border="true" value="settings[SETTINGS.EVENT_VILLAGE_CHANGE]" vertical="false" size="'56x28'"></div></td></tr><tr><td colspan="2"><span class="ff-cell-fix">{{ 'event_priority_add' | i18n:loc.ale:textObject }}</span></td><td><div switch-slider="" enabled="true" border="true" value="settings[SETTINGS.EVENT_PRIORITY_ADD]" vertical="false" size="'56x28'"></div></td></tr><tr><td colspan="2"><span class="ff-cell-fix">{{ 'event_ignored_village' | i18n:loc.ale:textObject }}</span></td><td><div switch-slider="" enabled="true" border="true" value="settings[SETTINGS.EVENT_IGNORED_VILLAGE]" vertical="false" size="'56x28'"></div></td></tr></table><table class="tbl-border-light tbl-content tbl-medium-height"><colgroup><col><col width="200px"></colgroup><tr><th colspan="2">{{ 'others' | i18n:loc.ale:textObjectCommon }}</th></tr><tr><td><span class="ff-cell-fix">{{ 'remote' | i18n:loc.ale:textObject }}</span></td><td><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.REMOTE_ID]"></td></tr><tr><td><span class="ff-cell-fix">{{ 'hotkey_switch' | i18n:loc.ale:textObject }}</span></td><td><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.HOTKEY_SWITCH]"></td></tr><tr><td><span class="ff-cell-fix">{{ 'hotkey_window' | i18n:loc.ale:textObject }}</span></td><td><input type="text" class="fit textfield-border text-center" ng-model="settings[SETTINGS.HOTKEY_WINDOW]"></td></tr></table></div><div class="logs rich-text" ng-show="selectedTab === TAB_TYPES.LOGS"><table class="status tbl-border-light"><colgroup><col width="135px"><col width="*"></colgroup><tbody><tr><td>{{ 'status' | i18n:loc.ale:textObjectCommon }}</td><td><span>{{ currentStatus | i18n:loc.ale:textObject }}</span></td></tr><tr><td>{{ 'selected_village' | i18n:loc.ale:textObject }}</td><td><span ng-show="selectedVillage" class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(selectedVillage.id)">{{ selectedVillage.name }} ({{ selectedVillage.x }}|{{ selectedVillage.y }})</span> <span ng-show="!selectedVillage">{{ 'none' | i18n:loc.ale:textObjectCommon }}</span></td></tr><tr><td>{{ 'last_attack' | i18n:loc.ale:textObject }}</td><td><span ng-show="lastAttack !== -1">{{ lastAttack | readableDateFilter:loc.ale:GAME_TIMEZONE:GAME_TIME_OFFSET }}</span> <span ng-show="lastAttack === -1">{{ 'none' | i18n:loc.ale:textObjectCommon }}</span></td></tr></tbody></table><h5 class="twx-section">{{ TAB_TYPES.LOGS | i18n:loc.ale:textObjectCommon }}</h5><div class="page-wrap" pagination="pagination"></div><p class="text-center" ng-show="!visibleLogs.length">{{ 'no_logs' | i18n:loc.ale:textObject }}</p><table class="log-list tbl-border-light tbl-striped" ng-show="visibleLogs.length"><colgroup><col width="180px"><col width="30px"><col></colgroup><tr ng-repeat="log in visibleLogs"><td>{{ log.time | readableDateFilter:loc.ale:GAME_TIMEZONE:GAME_TIME_OFFSET }}</td><td><span class="icon-bg-black icon-26x26-{{ log.icon }}"></span></td><td ng-if="log.type === LOG_TYPES.ATTACK"><span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(log.origin.id)" tooltip="" tooltip-content="{{ villagesLabel[log.origin.coords] }}">{{ villagesLabel[log.origin.coords] }}</span> {{ 'attack' | i18n:loc.ale:textObject }} <span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(log.target.id)" tooltip="" tooltip-content="{{ villagesLabel[log.target.coords] }}">{{ villagesLabel[log.target.coords] }}</span></td><td ng-if="log.type === LOG_TYPES.VILLAGE_SWITCH">{{ 'village_switch' | i18n:loc.ale:textObject }} <span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(log.village.id)" tooltip="" tooltip-content="{{ villagesLabel[log.village.coords] }}">{{ villagesLabel[log.village.coords] }}</span></td><td ng-if="log.type === LOG_TYPES.NO_PRESET">{{ 'no_preset' | i18n:loc.ale:textObject }}</td><td ng-if="log.type === LOG_TYPES.PRIORITY_TARGET"><span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(log.village.id)" tooltip="" tooltip-content="{{ villagesLabel[log.village.coords] }}">{{ villagesLabel[log.village.coords] }}</span> {{ 'priority_target' | i18n:loc.ale:textObject }}</td><td ng-if="log.type === LOG_TYPES.IGNORED_VILLAGE"><span class="village-link img-link icon-20x20-village btn btn-orange padded" ng-click="openVillageInfo(log.village.id)" tooltip="" tooltip-content="{{ villagesLabel[log.village.coords] }}">{{ villagesLabel[log.village.coords] }}</span> {{ 'ignored_village' | i18n:loc.ale:textObject }}</td></tr></table><div class="page-wrap" pagination="pagination"></div></div></div></div></div><footer class="win-foot"><ul class="list-btn list-center"><li ng-show="selectedTab === TAB_TYPES.SETTINGS"><a href="#" class="btn-border btn-orange" ng-click="saveSettings()">{{ 'save' | i18n:loc.ale:textObjectCommon }}</a></li><li ng-show="selectedTab === TAB_TYPES.LOGS"><a href="#" class="btn-border btn-orange" ng-click="clearLogs()">{{ 'clear_logs' | i18n:loc.ale:textObject }}</a></li><li><a href="#" ng-class="{false:'btn-green', true:'btn-red'}[running]" class="btn-border" ng-click="switchFarm()"><span ng-show="running">{{ 'pause' | i18n:loc.ale:textObjectCommon }}</span> <span ng-show="!running">{{ 'start' | i18n:loc.ale:textObjectCommon }}</span></a></li></ul></footer></div>`)
          interfaceOverflow.addStyle('#two-farmoverflow .settings table{margin-bottom:15px}#two-farmoverflow .settings input.textfield-border{padding-top:6px;width:219px;height:34px}#two-farmoverflow .settings input.textfield-border.fit{width:100%}#two-farmoverflow .settings span.select-wrapper{width:219px}#two-farmoverflow .settings .range-container{width:350px}#two-farmoverflow .logs .status tr{height:25px}#two-farmoverflow .logs .status td{padding:0 6px}#two-farmoverflow .logs .log-list{margin-bottom:10px}#two-farmoverflow .logs .log-list td{text-align:center}#two-farmoverflow .logs .log-list td .village-link{max-width:200px;white-space:nowrap;text-overflow:ellipsis}')
      }
  
      var buildWindow = function () {
          $scope = $rootScope.$new()
          $scope.textObject = textObject
          $scope.textObjectCommon = textObjectCommon
          $scope.SETTINGS = SETTINGS
          $scope.TAB_TYPES = TAB_TYPES
          $scope.LOG_TYPES = LOG_TYPES
          $scope.selectedTab = TAB_TYPES.SETTINGS
          $scope.selectedVillage = farmOverflow.getSelectedVillage()
          $scope.lastAttack = farmOverflow.getLastAttack()
          $scope.running = farmOverflow.isRunning()
          $scope.currentStatus = farmOverflow.getCurrentStatus()
          $scope.logs = farmOverflow.getLogs()
          $scope.villagesLabel = {}
          $scope.visibleLogs = []
          $scope.pagination = {
              count: $scope.logs.length,
              offset: 0,
              loader: updateVisibleLogs,
              limit: storageService.getPaginationLimit()
          }
  
          settings.injectSettings($scope)
          eventHandlers.updatePresets()
          eventHandlers.updateGroups()
          eventHandlers.updateSelectedVillage()
          eventHandlers.updateLastAttack()
          updateVisibleLogs()
          loadVillagesLabel()
  
          // scope functions
          $scope.selectTab = selectTab
          $scope.saveSettings = saveSettings
          $scope.clearLogs = clearLogs
          $scope.switchFarm = switchFarm
          $scope.jumpToVillage = mapService.jumpToVillage
          $scope.openVillageInfo = windowDisplayService.openVillageInfo
  
          eventScope = new EventScope('twoverflow_farm_window')
          eventScope.register(eventTypeProvider.ARMY_PRESET_UPDATE, eventHandlers.updatePresets, true)
          eventScope.register(eventTypeProvider.ARMY_PRESET_DELETED, eventHandlers.updatePresets, true)
          eventScope.register(eventTypeProvider.GROUPS_UPDATED, eventHandlers.updateGroups, true)
          eventScope.register(eventTypeProvider.GROUPS_CREATED, eventHandlers.updateGroups, true)
          eventScope.register(eventTypeProvider.GROUPS_DESTROYED, eventHandlers.updateGroups, true)
          eventScope.register(eventTypeProvider.FARM_START, eventHandlers.start)
          eventScope.register(eventTypeProvider.FARM_PAUSE, eventHandlers.pause)
          eventScope.register(eventTypeProvider.FARM_VILLAGES_UPDATE, eventHandlers.updateSelectedVillage)
          eventScope.register(eventTypeProvider.FARM_NEXT_VILLAGE, eventHandlers.updateSelectedVillage)
          eventScope.register(eventTypeProvider.FARM_SEND_COMMAND, eventHandlers.updateLastAttack)
          eventScope.register(eventTypeProvider.FARM_STATUS_CHANGE, eventHandlers.updateCurrentStatus)
          eventScope.register(eventTypeProvider.FARM_RESET_LOGS, eventHandlers.updateLogs)
          eventScope.register(eventTypeProvider.FARM_LOGS_RESETED, eventHandlers.resetLogsHandler)
          eventScope.register(eventTypeProvider.FARM_LOGS_UPDATED, eventHandlers.updateLogs)
          eventScope.register(eventTypeProvider.FARM_STEP_CYCLE_END, eventHandlers.stepCycleEndHandler)
          eventScope.register(eventTypeProvider.FARM_STEP_CYCLE_END_NO_VILLAGES, eventHandlers.stepCycleEndNoVillagesHandler)
          eventScope.register(eventTypeProvider.FARM_STEP_CYCLE_NEXT, eventHandlers.stepCycleNextHandler)
          eventScope.register(eventTypeProvider.FARM_ERROR, eventHandlers.errorHandler)
  
          windowManagerService.getScreenWithInjectedScope('!twoverflow_farm_window', $scope)
      }
  
      return init
  })
  
  define('two/farmOverflow/logTypes', [], function () {
      return {
          ATTACK: 'attack',
          VILLAGE_SWITCH: 'village_switch',
          NO_PRESET: 'no_preset',
          PRIORITY_TARGET: 'priority_target',
          IGNORED_VILLAGE: 'ignored_village'
      }
  })
  
  define('two/farmOverflow/settingsMap', [
      'two/farmOverflow/settings',
      'two/farmOverflow/settingsUpdate'
  ], function (
      SETTINGS,
      SETTINGS_UPDATE
  ) {
      return {
          [SETTINGS.PRESETS]: {
              default: [],
              updates: [SETTINGS_UPDATE.PRESET],
              disabledOption: true,
              inputType: 'select',
              multiSelect: true,
              type: 'presets'
          },
          [SETTINGS.GROUP_IGNORE]: {
              default: false,
              updates: [SETTINGS_UPDATE.GROUPS],
              disabledOption: true,
              inputType: 'select',
              type: 'groups'
          },
          [SETTINGS.GROUP_INCLUDE]: {
              default: [],
              updates: [SETTINGS_UPDATE.GROUPS, SETTINGS_UPDATE.TARGETS],
              disabledOption: true,
              inputType: 'select',
              multiSelect: true,
              type: 'groups'
          },
          [SETTINGS.GROUP_ONLY]: {
              default: [],
              updates: [SETTINGS_UPDATE.GROUPS, SETTINGS_UPDATE.VILLAGES, SETTINGS_UPDATE.TARGETS],
              disabledOption: true,
              inputType: 'select',
              multiSelect: true,
              type: 'groups'
          },
          [SETTINGS.RANDOM_BASE]: {
              default: 3,
              updates: []
          },
          [SETTINGS.COMMANDS_PER_VILLAGE]: {
              default: 48,
              updates: [SETTINGS_UPDATE.WAITING_VILLAGES]
          },
          [SETTINGS.PRIORITY_TARGETS]: {
              default: true,
              updates: []
          },
          [SETTINGS.IGNORE_ON_LOSS]: {
              default: true,
              updates: []
          },
          [SETTINGS.IGNORE_FULL_STORAGE]: {
              default: true,
              updates: [SETTINGS_UPDATE.FULL_STORAGE]
          },
          [SETTINGS.STEP_CYCLE]: {
              default: false,
              updates: [SETTINGS_UPDATE.VILLAGES]
          },
          [SETTINGS.STEP_CYCLE_NOTIFS]: {
              default: false,
              updates: []
          },
          [SETTINGS.STEP_CYCLE_INTERVAL]: {
              default: 0,
              updates: []
          },
          [SETTINGS.MAX_DISTANCE]: {
              default: 10,
              updates: [SETTINGS_UPDATE.TARGETS]
          },
          [SETTINGS.MIN_DISTANCE]: {
              default: 0,
              updates: [SETTINGS_UPDATE.TARGETS]
          },
          [SETTINGS.MIN_POINTS]: {
              default: 0,
              updates: [SETTINGS_UPDATE.TARGETS]
          },
          [SETTINGS.MAX_POINTS]: {
              default: 12500,
              updates: [SETTINGS_UPDATE.TARGETS]
          },
          [SETTINGS.MAX_TRAVEL_TIME]: {
              default: 60,
              updates: []
          },
          [SETTINGS.LOGS_LIMIT]: {
              default: 500,
              updates: [SETTINGS_UPDATE.LOGS]
          },
          [SETTINGS.EVENT_ATTACK]: {
              default: true,
              updates: [SETTINGS_UPDATE.LOGS]
          },
          [SETTINGS.EVENT_VILLAGE_CHANGE]: {
              default: true,
              updates: [SETTINGS_UPDATE.LOGS]
          },
          [SETTINGS.EVENT_PRIORITY_ADD]: {
              default: true,
              updates: [SETTINGS_UPDATE.LOGS]
          },
          [SETTINGS.EVENT_IGNORED_VILLAGE]: {
              default: true,
              updates: [SETTINGS_UPDATE.LOGS]
          },
          [SETTINGS.REMOTE_ID]: {
              default: 'remote',
              updates: []
          },
          [SETTINGS.HOTKEY_SWITCH]: {
              default: 'shift+z',
              updates: []
          },
          [SETTINGS.HOTKEY_WINDOW]: {
              default: 'z',
              updates: []
          }
      }
  })
  
  define('two/farmOverflow/settingsUpdate', function () {
      return {
          PRESET: 'preset',
          GROUPS: 'groups',
          TARGETS: 'targets',
          VILLAGES: 'villages',
          WAITING_VILLAGES: 'waiting_villages',
          FULL_STORAGE: 'full_storage',
          LOGS: 'logs'
      }
  })
  define('two/farmOverflow/settings', [], function () {
      return {
          PRESETS: 'presets',
          GROUP_IGNORE: 'group_ignore',
          GROUP_INCLUDE: 'group_include',
          GROUP_ONLY: 'group_only',
          RANDOM_BASE: 'random_base',
          COMMANDS_PER_VILLAGE: 'commands_per_village',
          PRIORITY_TARGETS: 'priority_targets',
          IGNORE_ON_LOSS: 'ignore_on_loss',
          IGNORE_FULL_STORAGE: 'ignore_full_storage',
          STEP_CYCLE: 'step_cycle',
          STEP_CYCLE_NOTIFS: 'step_cycle_notifs',
          STEP_CYCLE_INTERVAL: 'step_cycle_interval',
          MAX_DISTANCE: 'max_distance',
          MIN_DISTANCE: 'min_distance',
          MIN_POINTS: 'min_points',
          MAX_POINTS: 'max_points',
          MAX_TRAVEL_TIME: 'max_travel_time',
          LOGS_LIMIT: 'logs_limit',
          EVENT_ATTACK: 'event_attack',
          EVENT_VILLAGE_CHANGE: 'event_village_change',
          EVENT_PRIORITY_ADD: 'event_priority_add',
          EVENT_IGNORED_VILLAGE: 'event_ignored_village',
          REMOTE_ID: 'remote_id',
          HOTKEY_SWITCH: 'hotkey_switch',
          HOTKEY_WINDOW: 'hotkey_window'
      }
  })
  
  define('two/farmOverflow/Village', [
      'models/CommandListModel',
      'models/CommandModel',
      'conf/village'
  ], function (
      CommandListModel,
      CommandModel,
      VILLAGE_CONFIG
  ) {
      // 'READY_STATES' : {
      //     'COMPLETED'         : 'completed',
      //     'EFFECTS'           : 'effects',
      //     'BUILDINGS'         : 'buildings',
      //     'UNITS'             : 'units',
      //     'UNIT_QUEUE'        : 'unit_queue',
      //     'RESOURCES'         : 'resources',
      //     'TRADES'            : 'trades',
      //     'TIMELINE'          : 'timeline',
      //     'BUILDING_QUEUE'    : 'buildingQueue',
      //     'COMMANDS'          : 'commands',
      //     'OWN_COMMANDS'      : 'ownCommands',
      //     'FOREIGN_COMMANDS'  : 'foreignCommands',
      //     'SCOUTING'          : 'scouting',
      //     'SCOUTING_COMMANDS' : 'scoutingCommands'
      // }
  
      /**
       * @class
       *
       * @param {VillageModel} original - Objeto original da aldeia.
       */
      function Village (original) {
          this.original = original
          this.id = original.data.villageId
          this.x = original.data.x
          this.y = original.data.y
          this.name = original.data.name
          this.units = original.unitInfo.units
          this.position = original.getPosition()
      }
  
      Village.prototype.countCommands = function () {
          var commands = this.original.getCommandListModel()
  
          // commands.getOutgoingCommands(true) obtem a lista de comandos
          // com exceção dos comandos de espionagem.
          return commands.getOutgoingCommands(true).length
      }
  
      Village.prototype.updateCommands = function (callback) {
          var self = this
  
          socketService.emit(routeProvider.GET_OWN_COMMANDS, {
              village_id: self.id
          }, function (data) {
              var commandList = new CommandListModel([], self.id)
  
              for (var i = 0; i < data.commands.length; i++) {
                  var command = new CommandModel(data.commands[i])
  
                  commandList.add(command)
              }
  
              self.original.setCommandListModel(commandList)
  
              callback()
          })
      }
  
      Village.prototype.commandsLoaded = function () {
          return this.original.isReady(VILLAGE_CONFIG.OWN_COMMANDS)
      }
  
      Village.prototype.unitsLoaded = function () {
          return this.original.isReady(VILLAGE_CONFIG.UNITS)
      }
  
      Village.prototype.loaded = function () {
          if (!this.original.isReady()) {
              return false
          }
  
          if (!this.original.isInitialized()) {
              return false
          }
  
          return this.commandsLoaded() && this.unitsLoaded()
      }
  
      Village.prototype.load = function (callback) {
          var self = this
  
          return villageService.ensureVillageDataLoaded(this.id, function () {
              if (!self.original.isInitialized()) {
                  villageService.initializeVillage(self.original)
              }
  
              callback()
          })
      }
  
      return Village
  })
  
  require([
      'two/language',
      'two/ready',
      'two/farmOverflow',
      'two/farmOverflow/ui',
      'two/farmOverflow/events'
  ], function (
      twoLanguage,
      ready,
      farmOverflow,
      farmOverflowInterface
  ) {
      if (farmOverflow.isInitialized()) {
          return false
      }
  
      ready(function () {
          twoLanguage.add('farm_overflow', {"cs_cz":{"farm_overflow":{"attacking":"Útočím.","paused":"Pozastaven.","command_limit":"Byl dosažen limit 50 útoků, vyčkávám na návrat.","last_attack":"Poslední útok","village_switch":"Měním na vesnici","no_preset":"Žádné předvolby nenalezeny.","no_selected_village":"Žádné dostupné vesnice.","no_units":"Ve vesnici nejsou jednotky, vyčkávám na návrat.","no_units_no_commands":"Žádná vesnice nemá jednotky nebo příkaz k návratu.","no_villages":"Žádné dostupné vesnice, vyčkávám na návrat.","preset_first":"Nejprve vyberte předvolbu!","selected_village":"Vesnice vybrána","loading_targets":"Náčítám cíle...","checking_targets":"Kontroluji cíle...","restarting_commands":"Restartuji příkaz...","ignored_village":"cíl ignorován! (zapřičiňuje ztráty)","priority_target":"přidán jako prioritní cíl.","analyse_targets":"Analizuji cíle.","step_cycle_restart":"Restarting the cycle of commands..","step_cycle_end":"The list of villages ended, waiting for the next run.","step_cycle_end_no_villages":"No villages available to start the cycle.","step_cycle_next":"The list of villages is over, next cycle: %{time}.","step_cycle_next_no_villages":"No village available to start the cycle, next cycle: %{time}.","full_storage":"The storage of the village is full.","farm_paused":"FarmOverflow pozastaven.","farm_started":"FarmOverflow spuštěn.","groups_presets":"Groups & presets","presets":"Attack with the presets","group_ignored":"Ignore villages from group","group_include":"Include villages from groups","group_only":"Attack only with villages from groups","random_base":"Random interval between attacks","commands_per_village":"Commands limit per village","priority_targets":"Prioritize targets with full loot","ignore_on_loss":"Ignore target that cause loss","ignore_full_storage":"Do not farm with villages with full storage","step_cycle_header":"Step Cycle Settings","step_cycle":"Enable Step Cycle","step_cycle_interval":"Interval between cycles (minutes)","step_cycle_notifs":"Cycle notifications","target_filters":"Target Filters","min_distance":"Minimum distance","max_distance":"Maximum distance","min_points":"Minimum points","max_points":"Maximum points","max_travel_time":"Maximum travel time (minutes)","logs_limit":"Limit of logs","event_attack":"Log attacks","event_village_change":"Log village changes","event_priority_add":"Log priority targets","event_ignored_village":"Log ignored villages","remote":"Remote Control Message Subject","hotkey_switch":"Start/pause hotkey","hotkey_window":"Open window hotkey","settings_saved":"Settings saved!","misc":"Miscellaneous","attack":"attack","no_logs":"No logs registered","clear_logs":"Clear logs","reseted_logs":"Registered logs reseted."}},"de_de":{"farm_overflow":{"attacking":"Angreifen.","paused":"Paused.","command_limit":"Maximal 50 Farmtrupps, bitte warte auf die Rückkehr einiger Trupps.","last_attack":"Letzter Angriff","village_switch":"Wechsle zu Dorf","no_preset":"Keine Vorlage verfügbar.","no_selected_village":"Keine Dörfer verfügbar.","no_units":"Keine Einheiten im Dorf, wartet auf die Rückkehr von Trupps.","no_units_no_commands":"Kein Dorf hat Einheiten oder zurückkehrende Befehle.","no_villages":"Keine Dörfer verfügbar, warte auf Rückkehr von Befehlen.","preset_first":"Konfigurieren Sie zuerst eine Standardvorlage!","selected_village":"Ausgewähltes Dorf","loading_targets":"Lade Ziele...","checking_targets":"Prüfung Ziele...","restarting_commands":"Befehl wird neu gestartet...","ignored_village":"zur ignorierliste hinzugefügt","priority_target":"zur prioritätenliste hinzugefügt","analyse_targets":"Ziele werden analysiert.","step_cycle_restart":"Starte den Befehlszyklus neu..","step_cycle_end":"Dorfliste abgearbeitet, warte auf den nächsten Durchlauf.","step_cycle_end_no_villages":"Keine Dörfer verfügbar um den Zyklus zu starten.","step_cycle_next":"Dorfliste abgearbeitet, nächster Durchlauf: %d.","step_cycle_next_no_villages":"Kein Dorf verfügbar um den Durchlauf zu starten, nächster Durchlauf: %d.","full_storage":"Der Speicher des Dorfes ist voll.","farm_paused":"FarmOverflow angehalten.","farm_started":"FarmOverflow angehalten.","groups_presets":"Gruppen & vorlagen","presets":"Vorlagen","group_ignored":"Ignoriere Dorfgruppe","group_include":"Dorfgruppe umfassen","group_only":"Einzige Dorfgruppe","random_base":"Zufälliges Intervall","commands_per_village":"Befehlslimit","priority_targets":"Priorisiere Ziele, welche noch Rohstoffe haben","ignore_on_loss":"Ignorieren bei Verlust","ignore_full_storage":"Volle Speicher überspringen","step_cycle_header":"Farmzyklus","step_cycle":"Farmzyklus aktivieren","step_cycle_interval":"Intervall","step_cycle_notifs":"Benachrichtigungen","target_filters":"Zielfilter","min_distance":"Minimale Distanz","max_distance":"Maximale Distanz","min_points":"Minimale Punktzahl des Ziels","max_points":"Minimale Punktzahl des Ziels","max_travel_time":"Maximale Laufzeit","logs_limit":"Protokoll Limit","event_attack":"Angriffe protokollieren","event_village_change":"Dorfwechsel protokollieren","event_priority_add":"Priorisierte Ziele protokollieren","event_ignored_village":"Ignorierte Dörfer protokollieren","remote":"Betreff der Kontrollnachricht","hotkey_switch":"Hotkey um zu starten/stoppen","hotkey_window":"Hotkey um das Fenster zu öffnen","settings_saved":"Einstellungen gespeichert!","misc":"Verschiedenes","attack":"attacke","no_logs":"Keine protokolle registriert","clear_logs":"Protokolle löschen","reseted_logs":"Protokolle wurden zurückgesetzt."}},"en_us":{"farm_overflow":{"attacking":"Attacking.","paused":"Paused.","command_limit":"Limit of 50 attacks reached, waiting return.","last_attack":"Last attack","village_switch":"Changing to village","no_preset":"No presets avaliable.","no_selected_village":"No villages avaliable.","no_units":"No units avaliable in village, waiting attacks return.","no_units_no_commands":"No villages has units or commands returning.","no_villages":"No villages avaliable, waiting attacks return.","preset_first":"Set a preset first!","selected_village":"Village selected","loading_targets":"Loading targets...","checking_targets":"Checking targets...","restarting_commands":"Restarting commands...","ignored_village":"added to the ignored list","priority_target":"added to priorities.","analyse_targets":"Analysing targets.","step_cycle_restart":"Restarting the cycle of commands..","step_cycle_end":"The list of villages ended, waiting for the next run.","step_cycle_end_no_villages":"No villages available to start the cycle.","step_cycle_next":"The list of villages is over, next cycle: %d.","step_cycle_next_no_villages":"No village available to start the cycle, next cycle: %d.","full_storage":"The storage of the village is full.","farm_paused":"FarmOverflow paused.","farm_started":"FarmOverflow started.","groups_presets":"Groups & presets","presets":"Attack with the presets","group_ignored":"Ignore villages from group","group_include":"Include villages from groups","group_only":"Attack only with villages from groups","random_base":"Random interval between attacks","commands_per_village":"Commands limit per village","priority_targets":"Prioritize targets with full loot","ignore_on_loss":"Ignore target that cause loss","ignore_full_storage":"Do not farm with villages with full storage","step_cycle_header":"Step Cycle Settings","step_cycle":"Enable Step Cycle","step_cycle_interval":"Interval between cycles (minutes)","step_cycle_notifs":"Cycle notifications","target_filters":"Target Filters","min_distance":"Targets minimum distance","max_distance":"Targets maximum distance","min_points":"Targets minimum points","max_points":"Targets maximum points","max_travel_time":"Maximum travel time (minutes)","logs_limit":"Maximum length of task logs","event_attack":"Show task logs of attacks","event_village_change":"Show task logs of village's changes","event_priority_add":"Show task logs of priority targets","event_ignored_village":"Show task logs of ignored villages","remote":"Control via message (subject)","hotkey_switch":"Hotkey to start/pause","hotkey_window":"Hotkey to open window","settings_saved":"Settings saved!","misc":"Miscellaneous","attack":"attack","no_logs":"No logs registered","clear_logs":"Clear logs","reseted_logs":"Registered logs reseted."}},"es_es":{"farm_overflow":{"attacking":"Atacando.","paused":"Paused.","command_limit":"Limite de 50 ataques alcanzados, esperando retorno.","last_attack":"Último ataque","village_switch":"Cambiando para villa","no_preset":"No presets avaliable.","no_selected_village":"No villages avaliable.","no_units":"No units avaliable in village, waiting attacks return.","no_units_no_commands":"No villages has units or commands returning.","no_villages":"No villages avaliable, waiting attacks return.","preset_first":"Set a preset first!","selected_village":"Village selected","loading_targets":"Loading targets...","checking_targets":"Checking targets...","restarting_commands":"Restarting commands...","ignored_village":"added to the ignored list","priority_target":"added to priorities.","analyse_targets":"Analysing targets.","step_cycle_restart":"Restarting the cycle of commands..","step_cycle_end":"The list of villages ended, waiting for the next run.","step_cycle_end_no_villages":"No villages available to start the cycle.","step_cycle_next":"The list of villages is over, next cycle: %{time}.","step_cycle_next_no_villages":"No village available to start the cycle, next cycle: %{time}.","full_storage":"The storage of the village is full.","farm_paused":"FarmOverflow paused.","farm_started":"FarmOverflow started.","groups_presets":"Groups & presets","presets":"Attack with the presets","group_ignored":"Ignore villages from group","group_include":"Include villages from groups","group_only":"Attack only with villages from groups","random_base":"Random interval between attacks","commands_per_village":"Commands limit per village","priority_targets":"Prioritize targets with full loot","ignore_on_loss":"Ignore target that cause loss","ignore_full_storage":"Do not farm with villages with full storage","step_cycle_header":"Step Cycle Settings","step_cycle":"Enable Step Cycle","step_cycle_interval":"Interval between cycles (minutes)","step_cycle_notifs":"Cycle notifications","target_filters":"Target Filters","min_distance":"Minimum distance","max_distance":"Maximum distance","min_points":"Minimum points","max_points":"Maximum points","max_travel_time":"Maximum travel time (minutes)","logs_limit":"Limit of logs","event_attack":"Log attacks","event_village_change":"Log village changes","event_priority_add":"Log priority targets","event_ignored_village":"Log ignored villages","remote":"Remote Control Message Subject","hotkey_switch":"Start/pause hotkey","hotkey_window":"Open window hotkey","settings_saved":"Settings saved!","misc":"Miscellaneous","attack":"attack","no_logs":"No logs registered","clear_logs":"Clear logs","reseted_logs":"Registered logs reseted."}},"pl_pl":{"farm_overflow":{"attacking":"Atakuje.","paused":"Zatrzymany.","command_limit":"Limit 50 ataków osiągnięty, oczekiwanie na powrót wojsk.","last_attack":"Ostatni atak","village_switch":"Przejście do wioski","no_preset":"Brak dostępnych szablonów.","no_selected_village":"Brak dostępnych wiosek.","no_units":"Brak dostępnych jednostek w wiosce, oczekiwanie na powrót wojsk.","no_units_no_commands":"Brak jednostek w wioskach lub powracających wojsk.","no_villages":"Brak dostępnych wiosek, oczekiwanie na powrót wojsk.","preset_first":"Wybierz najpierw szablon!","selected_village":"Wybrana wioska","loading_targets":"Ładowanie celów...","checking_targets":"Sprawdzanie celów...","restarting_commands":"Restartowanie poleceń...","ignored_village":"dodany do listy pominiętych","priority_target":"dodany do priorytetowych.","analyse_targets":"Analizowanie celów.","step_cycle_restart":"Restartowanie cyklu poleceń...","step_cycle_end":"Lista wiosek zakończona, oczekiwanie na następny cykl.","step_cycle_end_no_villages":"Brak wiosek do rozpoczęcia cyklu.","step_cycle_next":"Lista wiosek się skończyła, następny cykl: %d.","step_cycle_next_no_villages":"Brak wioski do rozpoczęcia cyklu, następny cykl: %d.","full_storage":"Magazyn w wiosce jest pełny","farm_paused":"Farmer zatrzymany","farm_started":"Farmer uruchomiony","groups_presets":"Grupy i szablony","presets":"Szablony","group_ignored":"Pomijaj wioski z grupy","group_include":"Dodaj wioski z grupy","group_only":"Tylko wioski z grupy","random_base":"Domyślny odstęp (sek)","commands_per_village":"Limit poleceń","priority_targets":"Priorytyzuj cele","ignore_on_loss":"Pomijaj cele jeśli straty","ignore_full_storage":"Pomijaj wioski jeśli magazyn pełny","step_cycle_header":"Cykl Farmienia","step_cycle":"Włącz Cykl farmienia","step_cycle_interval":"Odstęp między cyklami (minuty)","step_cycle_notifs":"Powiadomienia","target_filters":"Filtry celów","min_distance":"Minimalna odległość","max_distance":"Maksymalna odległość","min_points":"Minimalna liczba punktów","max_points":"Maksymalna liczba punktów","max_travel_time":"Maksymalny czas podróży (minuty)","logs_limit":"Limit logów","event_attack":"Logi ataków","event_village_change":"Logi zmiany wiosek","event_priority_add":"Logi celów priorytetowych","event_ignored_village":"Logi pominiętych wiosek","remote":"Sterowanie Zdalne za pomocą wiadomości PW","hotkey_switch":"Skrót Start/Pauza","hotkey_window":"Skrót okna Farmera","settings_saved":"Ustawienia zapisane!","misc":"Różne","attack":"atakuje","no_logs":"Brak zarejestrowanych logów","clear_logs":"Wyczyść logi","reseted_logs":"Zarejestrowane logi zostały wyczyszczone."}},"pt_br":{"farm_overflow":{"attacking":"Atacando.","paused":"Pausado.","command_limit":"Limite de 50 ataques atingido, aguardando retorno.","last_attack":"Último ataque","village_switch":"Alternando para a aldeia","no_preset":"Nenhuma predefinição disponível.","no_selected_village":"Nenhuma aldeia disponível.","no_units":"Sem unidades na aldeia, aguardando ataques retornarem.","no_units_no_commands":"Nenhuma aldeia tem tropas nem ataques retornando.","no_villages":"Nenhuma aldeia disponível, aguardando ataques retornarem.","preset_first":"Configure uma predefinição primeiro!","selected_village":"Aldeia selecionada","loading_targets":"Carregando alvos...","checking_targets":"Checando alvos...","restarting_commands":"Reiniciando comandos...","ignored_village":"adicionado a lista de ignorados.","priority_target":"adicionado as prioridades.","analyse_targets":"Analisando alvos.","step_cycle_restart":"Reiniciando o ciclo de comandos..","step_cycle_end":"A lista de aldeias acabou, esperando próxima execução.","step_cycle_end_no_villages":"Nenhuma aldeia disponível para iniciar o ciclo.","step_cycle_next":"A lista de aldeias acabou, próximo ciclo: %d.","step_cycle_next_no_villages":"Nenhuma aldeia disponível para iniciar o ciclo, próximo ciclo: %d.","full_storage":"O armazém da aldeia está cheio.","farm_paused":"FarmOverflow pausado.","farm_started":"FarmOverflow iniciado.","groups_presets":"Grupos & predefinições","presets":"Atacar com as predefinições","group_ignored":"Ignorar aldeias do grupo","group_include":"Include aldeias dos grupos","group_only":"Atacar apenas com aldeias dos grupos","random_base":"Intervalo aleatório entre ataques","commands_per_village":"Limite de comandos por aldeia","priority_targets":"Priorizar alvos com saques cheios","ignore_on_loss":"Ignorar alvos que causam perdas","ignore_full_storage":"Ignorar armazéns lotados","step_cycle_header":"Configurações de Ciclos","step_cycle":"Ativar Ciclo","step_cycle_interval":"Intervalo entre ciclos (minutos)","step_cycle_notifs":"Notificações de ciclos","target_filters":"Filtro de Alvos","min_distance":"Distância mínima","max_distance":"Distância máxima","min_points":"Pontuação mínima","max_points":"Pontuação máxima","max_travel_time":"Tempo máximo de viagem (minutos)","logs_limit":"Limite de registros","event_attack":"Registrar ataques","event_village_change":"Registrar troca de aldeias","event_priority_add":"Registrar alvos prioritarios","event_ignored_village":"Registrar alvos ignorados","remote":"Controle Remoto - Mensagem","hotkey_switch":"Atalho para inicar/pausar","hotkey_window":"Atalho para abrir janela","settings_saved":"Configurações salvas!","misc":"Diversos","attack":"ataca","no_logs":"Nenhum evento registrado","clear_logs":"Limpar eventos","reseted_logs":"Registro de eventos resetado."}}})
          farmOverflow.init()
          farmOverflowInterface()
      }, ['map'])
  })
  
  define('two/minimap', [
      'two/minimap/actionTypes',
      'two/minimap/settings',
      'two/minimap/settingsMap',
      'two/utils',
      'two/ready',
      'queues/EventQueue',
      'Lockr',
      'struct/MapData',
      'helper/mapconvert',
      'cdn',
      'conf/colors',
      'conf/colorGroups',
      'conf/conf',
      'version',
      'two/Settings'
  ], function (
      ACTION_TYPES,
      SETTINGS,
      SETTINGS_MAP,
      utils,
      ready,
      eventQueue,
      Lockr,
      mapData,
      mapconvert,
      cdn,
      colors,
      colorGroups,
      conf,
      gameVersion,
      Settings
  ) {
      var enableRendering = false
      var highlights = {}
      var villageSize = 5
      var villageMargin = 1
      var rhex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/
      var cache = {
          village: {},
          character: {},
          tribe: {}
      }
      // Cached villages from previous loaded maps.
      // Used to draw the "ghost" villages on the map.
      var cachedVillages = {}
      // Data of the village that the user is hovering on the minimap.
      var hoverVillage = null
      var $viewport
      var $viewportContext
      var $viewportCache
      var $viewportCacheContext
      var $cross
      var $crossContext
      // Game main canvas map.
      // Used to calculate the position cross position based on canvas size.
      var $map
      var $player
      var $tribeRelations
      var selectedVillage
      var currentPosition = {}
      var frameSize = {}
      var dataView
      var settings = {}
      var STORAGE_KEYS = {
          CACHE_VILLAGES: 'minimap_cache_villages',
          SETTINGS: 'minimap_settings'
      }
      var colorService = injector.get('colorService')
      var allowJump = true
      var allowMove = false
      var dragStart = {}
  
      /**
       * Calcule the coords from clicked position in the canvas.
       *
       * @param {Object} event - Canvas click event.
       * @return {Object} X and Y coordinates.
       */
      var getCoords = function (event) {
          var villageBlock = minimap.getVillageBlock()
          var villageOffsetX = minimap.getVillageAxisOffset()
          var rawX = Math.ceil(currentPosition.x + event.offsetX)
          var rawY = Math.ceil(currentPosition.y + event.offsetY)
          var adjustLine = Math.floor((rawY / villageBlock) % 2)
  
          if (adjustLine % 2) {
              rawX -= villageOffsetX
          }
          
          rawX -= rawX % villageBlock
          rawY -= rawY % villageBlock
  
          return {
              x: Math.ceil((rawX - frameSize.x / 2) / villageBlock),
              y: Math.ceil((rawY - frameSize.y / 2) / villageBlock)
          }
      }
  
      /**
       * Convert pixel wide map position to coords
       *
       * @param {Number} x - X pixel position.
       * @param {Number} y - Y pixel position.
       * @return {Object} Y and Y coordinates.
       */
      var pixel2Tiles = function (x, y) {
          return {
              x: (x / conf.TILESIZE.x),
              y: (y / conf.TILESIZE.y / conf.TILESIZE.off)
          }
      }
  
      /**
       * Calculate the coords based on zoom.
       *
       * @param {Array[x, y, canvasW, canvasH]} rect - Coords and canvas size.
       * @param {Number} zoom - Current zoom used to display the game original map.
       * @return {Array} Calculated coords.
       */
      var convert = function (rect, zoom) {
          zoom = 1 / (zoom || 1)
  
          var xy = pixel2Tiles(rect[0] * zoom, rect[1] * zoom)
          var wh = pixel2Tiles(rect[2] * zoom, rect[3] * zoom)
          
          return [
              xy.x - 1,
              xy.y - 1,
              (wh.x + 3) || 1,
              (wh.y + 3) || 1
          ]
      }
  
      /**
       * @param {Array} villages
       * @param {String=} _color - Force the village to use the
       *   specified color.
       */
      var drawVillages = function (villages, _color) {
          var v
          var x
          var y
          var color
          var pid = $player.getId()
          var tid = $player.getTribeId()
          var villageBlock = minimap.getVillageBlock()
          var villageSize = minimap.getVillageSize()
          var villageOffsetX = minimap.getVillageAxisOffset()
          var villageColors = $player.getVillagesColors()
          var i
  
          for (i = 0; i < villages.length; i++) {
              v = villages[i]
  
              // meta village
              if (v.id < 0) {
                  continue
              }
  
              if (_color) {
                  color = _color
                  
                  x = v[0] * villageBlock
                  y = v[1] * villageBlock
  
                  if (v[1] % 2) {
                      x += villageOffsetX
                  }
              } else {
                  x = v.x * villageBlock
                  y = v.y * villageBlock
  
                  if (v.y % 2) {
                      x += villageOffsetX
                  }
  
                  if (settings.getSetting(SETTINGS.SHOW_ONLY_CUSTOM_HIGHLIGHTS)) {
                      if (v.character_id in highlights.character) {
                          color = highlights.character[v.character_id]
                      } else if (v.tribe_id in highlights.tribe) {
                          color = highlights.tribe[v.tribe_id]
                      } else {
                          continue
                      }
                  } else {
                      if (v.character_id === null) {
                          if (!settings.getSetting(SETTINGS.SHOW_BARBARIANS)) {
                              continue
                          }
  
                          color = villageColors.barbarian
                      } else {
                          if (v.character_id === pid) {
                              if (v.id === selectedVillage.getId() && settings.getSetting(SETTINGS.HIGHLIGHT_SELECTED)) {
                                  color = villageColors.selected
                              } else if (v.character_id in highlights.character) {
                                  color = highlights.character[v.character_id]
                              } else if (settings.getSetting(SETTINGS.HIGHLIGHT_OWN)) {
                                  color = villageColors.player
                              } else {
                                  color = villageColors.ugly
                              }
                          } else {
                              if (v.character_id in highlights.character) {
                                  color = highlights.character[v.character_id]
                              } else if (v.tribe_id in highlights.tribe) {
                                  color = highlights.tribe[v.tribe_id]
                              } else if (tid && tid === v.tribe_id && settings.getSetting(SETTINGS.HIGHLIGHT_DIPLOMACY)) {
                                  color = villageColors.tribe
                              } else if ($tribeRelations && settings.getSetting(SETTINGS.HIGHLIGHT_DIPLOMACY)) {
                                  if ($tribeRelations.isAlly(v.tribe_id)) {
                                      color = villageColors.ally
                                  } else if ($tribeRelations.isEnemy(v.tribe_id)) {
                                      color = villageColors.enemy
                                  } else if ($tribeRelations.isNAP(v.tribe_id)) {
                                      color = villageColors.friendly
                                  } else {
                                      color = villageColors.ugly
                                  }
                              } else {
                                  color = villageColors.ugly
                              }
                          }
                      }
                  }
              }
  
              $viewportCacheContext.fillStyle = color
              $viewportCacheContext.fillRect(x, y, villageSize, villageSize)
          }
      }
  
      var drawGrid = function () {
          var binUrl = cdn.getPath(conf.getMapPath())
          var villageBlock = minimap.getVillageBlock()
          var villageOffsetX = Math.round(villageBlock / 2)
          var tile
          var x
          var y
  
          utils.xhrGet(binUrl, function (bin) {
              dataView = new DataView(bin)
  
              for (x = 1; x < 999; x++) {
                  for (y = 1; y < 999; y++) {
                      tile = mapconvert.toTile(dataView, x, y)
                      
                      // is border
                      if (tile.key.b) {
                          // is continental border
                          if (tile.key.c) {
                              $viewportCacheContext.fillStyle = settings.getSetting(SETTINGS.COLOR_CONTINENT)
                              $viewportCacheContext.fillRect(x * villageBlock + villageOffsetX - 1, y * villageBlock + villageOffsetX - 1, 3, 1)
                              $viewportCacheContext.fillRect(x * villageBlock + villageOffsetX, y * villageBlock + villageOffsetX - 2, 1, 3)
                          } else {
                              $viewportCacheContext.fillStyle = settings.getSetting(SETTINGS.COLOR_PROVINCE)
                              $viewportCacheContext.fillRect(x * villageBlock + villageOffsetX, y * villageBlock + villageOffsetX - 1, 1, 1)
                          }
                      }
                  }
              }
          }, 'arraybuffer')
      }
  
      var drawLoadedVillages = function () {
          drawVillages(mapData.getTowns())
      }
  
      var drawCachedVillages = function () {
          var x
          var y
          var i
          var xx
          var yy
          var village
          var villageBlock = minimap.getVillageBlock()
          var villageSize = minimap.getVillageSize()
          var villageOffsetX = minimap.getVillageAxisOffset()
  
          for (x in cachedVillages) {
              for (i = 0; i < cachedVillages[x].length; i++) {
                  y = cachedVillages[x][i]
                  xx = x * villageBlock
                  yy = y * villageBlock
  
                  if (y % 2) {
                      xx += villageOffsetX
                  }
  
                  $viewportCacheContext.fillStyle = settings.getSetting(SETTINGS.COLOR_GHOST)
                  $viewportCacheContext.fillRect(xx, yy, villageSize, villageSize)
              }
          }
      }
  
      /**
       * @param {Object} pos - Minimap current position plus center of canvas.
       */
      var drawViewport = function (pos) {
          $viewportContext.drawImage($viewportCache, -pos.x, -pos.y)
      }
  
      /**
       * @return {[type]} [description]
       */
      var clearViewport = function () {
          $viewportContext.clearRect(0, 0, $viewport.width, $viewport.height)
      }
  
      /**
       * @param {Object} pos - Minimap current position plus center of canvas.
       */
      var drawCross = function (pos) {
          var villageBlock = minimap.getVillageBlock()
          var lineSize = minimap.getLineSize()
          var mapPosition = minimap.getMapPosition()
  
          var x = ((mapPosition[0] + mapPosition[2] - 2) * villageBlock) - pos.x
          var y = ((mapPosition[1] + mapPosition[3] - 2) * villageBlock) - pos.y
  
          $crossContext.fillStyle = settings.getSetting(SETTINGS.COLOR_CROSS)
          $crossContext.fillRect(x | 0, 0, 1, lineSize)
          $crossContext.fillRect(0, y | 0, lineSize, 1)
      }
  
      var clearCross = function () {
          $crossContext.clearRect(0, 0, $cross.width, $cross.height)
      }
  
      var renderStep = function () {
          if(enableRendering) {
              var pos =  {
                  x: currentPosition.x - (frameSize.x / 2),
                  y: currentPosition.y - (frameSize.y / 2)
              }
  
              clearViewport()
              clearCross()
  
              drawViewport(pos)
  
              if (settings.getSetting(SETTINGS.SHOW_CROSS)) {
                  drawCross(pos)
              }
          }
  
          window.requestAnimationFrame(renderStep)
      }
  
      /**
       * @param {Number} sectors - Amount of sectors to be loaded,
       *   each sector has a size of 25x25 fields.
       * @param {Number=} x Optional load center X
       * @param {Number=} y Optional load center Y
       */
      var preloadSectors = function (sectors, _x, _y) {
          var size = sectors * 25
          var x = (_x || selectedVillage.getX()) - (size / 2)
          var y = (_y || selectedVillage.getY()) - (size / 2)
  
          mapData.loadTownDataAsync(x, y, size, size, function () {})
      }
  
      var cacheVillages = function (villages) {
          var i
          var v
  
          for (i = 0; i < villages.length; i++) {
              v = villages[i]
  
              // meta village
              if (v.id < 0) {
                  continue
              }
  
              if (!(v.x in cache.village)) {
                  cache.village[v.x] = {}
              }
  
              if (!(v.x in cachedVillages)) {
                  cachedVillages[v.x] = []
              }
  
              cache.village[v.x][v.y] = v.character_id || 0
              cachedVillages[v.x].push(v.y)
  
              if (v.character_id) {
                  if (v.character_id in cache.character) {
                      cache.character[v.character_id].push([v.x, v.y])
                  } else {
                      cache.character[v.character_id] = [[v.x, v.y]]
                  }
  
                  if (v.tribe_id) {
                      if (v.tribe_id in cache.tribe) {
                          cache.tribe[v.tribe_id].push(v.character_id)
                      } else {
                          cache.tribe[v.tribe_id] = [v.character_id]
                      }
                  }
              }
          }
  
          Lockr.set(STORAGE_KEYS.CACHE_VILLAGES, cachedVillages)
      }
  
      var onHoverVillage = function (coords, event) {
          var pid
  
          if (hoverVillage) {
              if (hoverVillage.x === coords.x && hoverVillage.y === coords.y) {
                  return false
              } else {
                  onBlurVillage()
              }
          }
  
          eventQueue.trigger(eventTypeProvider.MINIMAP_VILLAGE_HOVER, {
              village: mapData.getTownAt(coords.x, coords.y),
              event: event
          })
  
          hoverVillage = { x: coords.x, y: coords.y }
          pid = cache.village[coords.x][coords.y]
  
          if (pid) {
              highlightVillages(cache.character[pid])
          } else {
              highlightVillages([[coords.x, coords.y]])
          }
      }
  
      var onBlurVillage = function () {
          var pid
  
          if (!hoverVillage) {
              return false
          }
  
          pid = cache.village[hoverVillage.x][hoverVillage.y]
  
          if (pid) {
              unhighlightVillages(cache.character[pid])
          } else {
              unhighlightVillages([[hoverVillage.x, hoverVillage.y]])
          }
  
          hoverVillage = false
          eventQueue.trigger(eventTypeProvider.MINIMAP_VILLAGE_BLUR)
      }
  
      var highlightVillages = function (villages) {
          drawVillages(villages, settings.getSetting(SETTINGS.COLOR_QUICK_HIGHLIGHT))
      }
  
      var unhighlightVillages = function (villages) {
          var _villages = []
          var i
  
          for (i = 0; i < villages.length; i++) {
              _villages.push(mapData.getTownAt(villages[i][0], villages[i][1]))
          }
  
          drawVillages(_villages)
      }
  
      var quickHighlight = function (coords) {
          var village = mapData.getTownAt(coords.x, coords.y)
          var action = settings.getSetting(SETTINGS.RIGHT_CLICK_ACTION)
          var type
          var id
          var data = {}
  
          if (!village) {
              return false
          }
  
          switch (settings.getSetting(SETTINGS.RIGHT_CLICK_ACTION)) {
          case ACTION_TYPES.HIGHLIGHT_PLAYER:
              if (!village.character_id) {
                  return false
              }
  
              data.type = 'character'
              data.id = village.character_id
  
              break
          case ACTION_TYPES.HIGHLIGHT_TRIBE:
              if (!village.tribe_id) {
                  return false
              }
  
              data.type = 'tribe'
              data.id = village.tribe_id
  
              break
          }
  
          minimap.addHighlight(data, '#' + colors.palette.random().random())
      }
  
      var eventHandlers = {
          onCrossMouseDown: function (event) {
              event.preventDefault()
  
              allowJump = true
              allowMove = true
              dragStart = {
                  x: currentPosition.x + event.pageX,
                  y: currentPosition.y + event.pageY
              }
  
              if (hoverVillage) {
                  eventQueue.trigger(eventTypeProvider.MINIMAP_VILLAGE_CLICK, [hoverVillage, event])
  
                  // right click
                  if (event.which === 3) {
                      quickHighlight(hoverVillage)
                  }
              }
          },
          onCrossMouseUp: function () {
              allowMove = false
              dragStart = {}
  
              if (!allowJump) {
                  eventQueue.trigger(eventTypeProvider.MINIMAP_STOP_MOVE)
              }
          },
          onCrossMouseMove: function (event) {
              var coords
              var village
              var highlighted
  
              allowJump = false
  
              if (allowMove) {
                  currentPosition.x = dragStart.x - event.pageX
                  currentPosition.y = dragStart.y - event.pageY
                  eventQueue.trigger(eventTypeProvider.MINIMAP_START_MOVE)
              }
  
              coords = getCoords(event)
  
              if (coords.x in cache.village) {
                  if (coords.y in cache.village[coords.x]) {
                      village = mapData.getTownAt(coords.x, coords.y)
  
                      // ignore barbarian villages
                      if (!settings.getSetting(SETTINGS.SHOW_BARBARIANS) && !village.character_id) {
                          return false
                      }
  
                      // check if the village is custom highlighted
                      if (settings.getSetting(SETTINGS.SHOW_ONLY_CUSTOM_HIGHLIGHTS)) {
                          highlighted = false
  
                          if (village.character_id in highlights.character) {
                              highlighted = true
                          } else if (village.tribe_id in highlights.tribe) {
                              highlighted = true
                          }
  
                          if (!highlighted) {
                              return false
                          }
                      }
  
                      return onHoverVillage(coords, event)
                  }
              }
  
              onBlurVillage()
          },
          onCrossMouseLeave: function () {
              if (hoverVillage) {
                  onBlurVillage()
              }
  
              eventQueue.trigger(eventTypeProvider.MINIMAP_MOUSE_LEAVE)
          },
          onCrossMouseClick: function (event) {
              if (!allowJump) {
                  return false
              }
  
              var coords = getCoords(event)
              $rootScope.$broadcast(eventTypeProvider.MAP_CENTER_ON_POSITION, coords.x, coords.y, true)
          },
          onCrossMouseContext: function (event) {
              event.preventDefault()
              return false
          },
          onVillageData: function (event, data) {
              drawVillages(data.villages)
              cacheVillages(data.villages)
          },
          onHighlightChange: function () {
              highlights.tribe = colorService.getCustomColorsByGroup(colorGroups.TRIBE_COLORS) || {}
              highlights.character = colorService.getCustomColorsByGroup(colorGroups.PLAYER_COLORS) || {}
  
              drawLoadedVillages()
          },
          onSelectedVillageChange: function () {
              var old = {
                  id: selectedVillage.getId(),
                  x: selectedVillage.getX(),
                  y: selectedVillage.getY()
              }
  
              selectedVillage = $player.getSelectedVillage()
  
              drawVillages([{
                  character_id: $player.getId(),
                  id: old.id,
                  x: old.x,
                  y: old.y
              }, {
                  character_id: $player.getId(),
                  id: selectedVillage.getId(),
                  x: selectedVillage.getX(),
                  y: selectedVillage.getY()
              }])
          }
      }
  
      var minimap = {
          SETTINGS_MAP: SETTINGS_MAP,
          ACTION_TYPES: ACTION_TYPES
      }
  
      minimap.setVillageSize = function (value) {
          villageSize = value
      }
  
      minimap.getVillageSize = function () {
          return villageSize
      }
  
      minimap.setVillageMargin = function (value) {
          villageMargin = value
      }
  
      minimap.getVillageMargin = function () {
          return villageMargin
      }
  
      /**
       * Get the size used by each village on minimap in pixels.
       *
       * @return {Number}
       */
      minimap.getVillageBlock = function () {
          return villageSize + villageMargin
      }
  
      minimap.getLineSize = function () {
          return 1000 * (villageSize + villageMargin)
      }
  
      /**
       * Get the center position of a village icon.
       *
       * @return {Number}
       */
      minimap.getVillageAxisOffset = function () {
          return Math.round(villageSize / 2)
      }
  
      /**
       * @param {Object} item - Highlight item.
       * @param {String} item.type - village, player or tribe
       * @param {String} item.id - village/player/tribe id
       * @param {Number=} item.x - village X coord.
       * @param {Number=} item.y - village Y coord.
       * @param {String} color - Hex color
       *
       * @return {Boolean} true if successfully added
       */
      minimap.addHighlight = function (item, color) {
          var colorGroup
  
          if (!item || !item.type || !item.id) {
              eventQueue.trigger(eventTypeProvider.MINIMAP_HIGHLIGHT_ADD_ERROR_NO_ENTRY)
              return false
          }
  
          if (!rhex.test(color)) {
              eventQueue.trigger(eventTypeProvider.MINIMAP_HIGHLIGHT_ADD_ERROR_INVALID_COLOR)
              return false
          }
  
          highlights[item.type][item.id] = color
          colorGroup = item.type === 'character' ? colorGroups.PLAYER_COLORS : colorGroups.TRIBE_COLORS
          colorService.setCustomColorsByGroup(colorGroup, highlights[item.type])
          $rootScope.$broadcast(eventTypeProvider.GROUPS_VILLAGES_CHANGED)
  
          drawLoadedVillages()
  
          return true
      }
  
      minimap.removeHighlight = function (type, itemId) {
          var colorGroup = type === 'character' ? colorGroups.PLAYER_COLORS : colorGroups.TRIBE_COLORS
  
          if (highlights[type][itemId]) {
              delete highlights[type][itemId]
              colorService.setCustomColorsByGroup(colorGroup, highlights[type])
              $rootScope.$broadcast(eventTypeProvider.GROUPS_VILLAGES_CHANGED)
  
              drawLoadedVillages()
  
              return true
          }
  
          return false
      }
  
      minimap.getHighlight = function (type, item) {
          if (highlights[type].hasOwnProperty(item)) {
              return highlights[type][item]
          } else {
              return false
          }
      }
  
      minimap.getHighlights = function () {
          return highlights
      }
  
      minimap.eachHighlight = function (callback) {
          for (var type in highlights) {
              for (var id in highlights[type]) {
                  callback(type, id, highlights[type][id])
              }
          }
      }
  
      minimap.setViewport = function (element) {
          $viewport = element
          $viewport.style.background = settings.getSetting(SETTINGS.COLOR_BACKGROUND)
          $viewportContext = $viewport.getContext('2d')
      }
  
      minimap.setCross = function (element) {
          $cross = element
          $crossContext = $cross.getContext('2d')
      }
  
      minimap.setCurrentPosition = function (x, y) {
          var block = minimap.getVillageBlock()
  
          currentPosition.x = x * block + 50
          currentPosition.y = y * block + (1000 - ((document.body.clientHeight - 238) / 2)) + 50
      }
  
      /**
       * @return {Array}
       */
      minimap.getMapPosition = function () {
          var view
  
          if (!$map.width || !$map.height) {
              return false
          }
  
          if (gameVersion.product.major === 1 && gameVersion.product.minor < 94) {
              view = window.twx.game.map.engine.getView()
          } else {
              view = mapData.getMap().engine.getView()
          }
  
          return convert([
              -view.x,
              -view.y,
              $map.width / 2,
              $map.height / 2
          ], view.z)
      }
  
      minimap.getSettings = function () {
          return settings
      }
  
      minimap.update = function () {
          var villageBlock = minimap.getVillageBlock()
  
          $viewport.style.background = settings.getSetting(SETTINGS.COLOR_BACKGROUND)
          $viewportCacheContext.clearRect(0, 0, $viewportCache.width, $viewportCache.height)
  
          if (settings.getSetting(SETTINGS.SHOW_DEMARCATIONS)) {
              drawGrid()
          }
  
          if (settings.getSetting(SETTINGS.SHOW_GHOST_VILLAGES)) {
              drawCachedVillages()
          }
  
          drawLoadedVillages()
      }
  
      minimap.enableRendering = function () {
          enableRendering = true
      }
  
      minimap.disableRendering = function () {
          enableRendering = false
      }
  
      minimap.init = function () {
          minimap.initialized = true
          $viewportCache = document.createElement('canvas')
          $viewportCacheContext = $viewportCache.getContext('2d')
          
          settings = new Settings({
              settingsMap: SETTINGS_MAP,
              storageKey: STORAGE_KEYS.SETTINGS
          })
  
          settings.onSettingsChange(function (changes, update) {
              if (update) {
                  minimap.update()
              }
          })
  
          highlights.tribe = colorService.getCustomColorsByGroup(colorGroups.TRIBE_COLORS) || {}
          highlights.character = colorService.getCustomColorsByGroup(colorGroups.PLAYER_COLORS) || {}
      }
  
      minimap.run = function () {
          var villageBlock
  
          ready(function () {
              $map = document.getElementById('main-canvas')
              $player = modelDataService.getSelectedCharacter()
              $tribeRelations = $player.getTribeRelations()
              cachedVillages = Lockr.get(STORAGE_KEYS.CACHE_VILLAGES, {}, true)
              
              villageBlock = minimap.getVillageBlock()
              currentPosition.x = 500 * villageBlock
              currentPosition.y = 500 * villageBlock
  
              frameSize.x = 701
              frameSize.y = 2000
  
              $viewport.setAttribute('width', frameSize.x)
              $viewport.setAttribute('height', frameSize.y)
              $viewportContext.imageSmoothingEnabled = false
  
              $viewportCache.setAttribute('width', 1000 * villageBlock)
              $viewportCache.setAttribute('height', 1000 * villageBlock)
              $viewportCache.imageSmoothingEnabled = false
  
              $cross.setAttribute('width', frameSize.x)
              $cross.setAttribute('height', frameSize.y)
              $crossContext.imageSmoothingEnabled = false
  
              selectedVillage = $player.getSelectedVillage()
              currentPosition.x = selectedVillage.getX() * villageBlock
              currentPosition.y = selectedVillage.getY() * villageBlock
  
              if (settings.getSetting(SETTINGS.SHOW_DEMARCATIONS)) {
                  drawGrid()
              }
  
              if (settings.getSetting(SETTINGS.SHOW_GHOST_VILLAGES)) {
                  drawCachedVillages()
              }
  
              drawLoadedVillages()
              cacheVillages(mapData.getTowns())
              renderStep()
  
              $cross.addEventListener('mousedown', eventHandlers.onCrossMouseDown)
              $cross.addEventListener('mouseup', eventHandlers.onCrossMouseUp)
              $cross.addEventListener('mousemove', eventHandlers.onCrossMouseMove)
              $cross.addEventListener('mouseleave', eventHandlers.onCrossMouseLeave)
              $cross.addEventListener('click', eventHandlers.onCrossMouseClick)
              $cross.addEventListener('contextmenu', eventHandlers.onCrossMouseContext)
              $rootScope.$on(eventTypeProvider.MAP_VILLAGE_DATA, eventHandlers.onVillageData)
              $rootScope.$on(eventTypeProvider.VILLAGE_SELECTED_CHANGED, eventHandlers.onSelectedVillageChange)
              $rootScope.$on(eventTypeProvider.TRIBE_RELATION_CHANGED, drawLoadedVillages)
              $rootScope.$on(eventTypeProvider.GROUPS_VILLAGES_CHANGED, eventHandlers.onHighlightChange)
          }, ['initial_village', 'tribe_relations'])
      }
  
      return minimap
  })
  
  define('two/minimap/actionTypes', [], function () {
      return {
          HIGHLIGHT_PLAYER: 'highlight_player',
          HIGHLIGHT_TRIBE: 'highlight_tribe'
      }
  })
  
  define('two/minimap/events', [], function () {
      angular.extend(eventTypeProvider, {
          MINIMAP_HIGHLIGHT_ADD_ERROR_EXISTS: 'minimap_highlight_add_error_exists',
          MINIMAP_HIGHLIGHT_ADD_ERROR_NO_ENTRY: 'minimap_highlight_add_error_no_entry',
          MINIMAP_HIGHLIGHT_ADD_ERROR_INVALID_COLOR: 'minimap_highlight_add_error_invalid_color',
          MINIMAP_VILLAGE_CLICK: 'minimap_village_click',
          MINIMAP_VILLAGE_HOVER: 'minimap_village_hover',
          MINIMAP_VILLAGE_BLUR: 'minimap_village_blur',
          MINIMAP_MOUSE_LEAVE: 'minimap_mouse_leave',
          MINIMAP_START_MOVE: 'minimap_start_move',
          MINIMAP_STOP_MOVE: 'minimap_stop_move'
      })
  })
  
  define('two/minimap/ui', [
      'two/ui',
      'two/ui/button',
      'two/minimap',
      'two/minimap/actionTypes',
      'two/minimap/settingsMap',
      'two/minimap/settings',
      'two/utils',
      'two/EventScope',
      'two/Settings',
      'helper/util',
      'struct/MapData',
      'cdn',
      'conf/colors'
  ], function (
      interfaceOverflow,
      FrontButton,
      minimap,
      ACTION_TYPES,
      SETTINGS_MAP,
      SETTINGS,
      utils,
      EventScope,
      Settings,
      util,
      mapData,
      cdn,
      colors
  ) {
      var $scope
      var textObject = 'minimap'
      var textObjectCommon = 'common'
      var TAB_TYPES = {
          MINIMAP: 'minimap',
          HIGHLIGHTS: 'highlights',
          SETTINGS: 'settings'
      }
      var DEFAULT_TAB = TAB_TYPES.MINIMAP
      var $minimapCanvas
      var $crossCanvas
      var $minimapContainer
      var actionTypes = []
      var selectedActionType = {}
      var MapController
      var tooltipWrapper
      var windowWrapper
      var mapWrapper
      var tooltipWrapperSpacer = {}
      var tooltipTimeout
      var highlightNames = {
          character: {},
          tribe: {}
      }
      var settings
  
      var selectTab = function (tab) {
          $scope.selectedTab = tab
  
          if (tab === TAB_TYPES.MINIMAP) {
              minimap.enableRendering()
          } else {
              minimap.disableRendering()
          }
      }
  
      var appendCanvas = function () {
          $minimapContainer = document.querySelector('#two-minimap .minimap-container')
          $minimapContainer.appendChild($minimapCanvas)
          $minimapContainer.appendChild($crossCanvas)
      }
  
      var getTribeData = function (data, callback) {
          socketService.emit(routeProvider.TRIBE_GET_PROFILE, {
              tribe_id: data.id
          }, callback)
      }
      
      var getCharacterData = function (data, callback) {
          socketService.emit(routeProvider.CHAR_GET_PROFILE, {
              character_id: data.id
          }, callback)
      }
  
      var getVillageData = function (data, callback) {
          mapData.loadTownDataAsync(data.x, data.y, 1, 1, callback)
      }
  
      var updateHighlightNames = function () {
          Object.keys($scope.highlights.character).forEach(function (id) {
              if (id in highlightNames.character) {
                  return
              }
  
              getCharacterData({
                  id: id
              }, function (data) {
                  highlightNames.character[id] = data.character_name
              })
          })
  
          Object.keys($scope.highlights.tribe).forEach(function (id) {
              if (id in highlightNames.tribe) {
                  return
              }
  
              getTribeData({
                  id: id
              }, function (data) {
                  highlightNames.tribe[id] = data.name
              })
          })
      }
  
      var showTooltip = function (_, data) {
          tooltipTimeout = setTimeout(function () {
              var windowOffset
              var tooltipOffset
              var onTop
              var onLeft
  
              windowWrapper.appendChild(tooltipWrapper)
              tooltipWrapper.classList.remove('ng-hide')
  
              MapController.tt.name = data.village.name
              MapController.tt.x = data.village.x
              MapController.tt.y = data.village.y
              MapController.tt.province_name = data.village.province_name
              MapController.tt.points = data.village.points
              MapController.tt.character_name = data.village.character_name || '-'
              MapController.tt.character_points = data.village.character_points || 0
              MapController.tt.tribe_name = data.village.tribe_name || '-'
              MapController.tt.tribe_tag = data.village.tribe_tag || '-'
              MapController.tt.tribe_points = data.village.tribe_points || 0
              MapController.tt.morale = data.village.morale || 0
              MapController.tt.position = {}
              MapController.tt.position.x = data.event.pageX + 50
              MapController.tt.position.y = data.event.pageY + 50
              MapController.tt.visible = true
  
              tooltipOffset = tooltipWrapper.getBoundingClientRect()
              windowOffset = windowWrapper.getBoundingClientRect()
              tooltipWrapperSpacer.x = tooltipOffset.width + 50
              tooltipWrapperSpacer.y = tooltipOffset.height + 50
  
              onTop = MapController.tt.position.y + tooltipWrapperSpacer.y > windowOffset.top + windowOffset.height
              onLeft = MapController.tt.position.x + tooltipWrapperSpacer.x > windowOffset.width
  
              if (onTop) {
                  MapController.tt.position.y -= 50
              }
  
              tooltipWrapper.classList.toggle('left', onLeft)
              tooltipWrapper.classList.toggle('top', onTop)
          }, 50)
      }
  
      var hideTooltip = function () {
          clearTimeout(tooltipTimeout)
          MapController.tt.visible = false
          tooltipWrapper.classList.add('ng-hide')
          mapWrapper.appendChild(tooltipWrapper)
      }
  
      var openColorPalette = function (inputType, colorGroup, itemId, itemColor) {
          var modalScope = $rootScope.$new()
          var selectedColor
          var hideReset = true
          var settingId
  
          modalScope.colorPalettes = colors.palette
  
          if (inputType === 'setting') {
              settingId = colorGroup
              selectedColor = settings.getSetting(settingId)
              hideReset = false
  
              modalScope.submit = function () {
                  $scope.settings[settingId] = '#' + modalScope.selectedColor
                  modalScope.closeWindow()
              }
  
              modalScope.reset = function () {
                  $scope.settings[settingId] = settings.getDefault(settingId)
                  modalScope.closeWindow()
              }
          } else if (inputType === 'add_custom_highlight') {
              selectedColor = $scope.addHighlightColor
  
              modalScope.submit = function () {
                  $scope.addHighlightColor = '#' + modalScope.selectedColor
                  modalScope.closeWindow()
              }
          } else if (inputType === 'edit_custom_highlight') {
              selectedColor = $scope.highlights[colorGroup][itemId]
  
              modalScope.submit = function () {
                  minimap.addHighlight({
                      id: itemId,
                      type: colorGroup
                  }, '#' + modalScope.selectedColor)
                  modalScope.closeWindow()
              }
          }
  
          modalScope.selectedColor = selectedColor.replace('#', '')
          modalScope.hasCustomColors = true
          modalScope.hideReset = hideReset
  
          modalScope.finishAction = function ($event, color) {
              modalScope.selectedColor = color
          }
  
          windowManagerService.getModal('modal_color_palette', modalScope)
      }
  
      var addCustomHighlight = function () {
          minimap.addHighlight($scope.selectedHighlight, $scope.addHighlightColor)
      }
  
      var saveSettings = function () {
          settings.setSettings(settings.decodeSettings($scope.settings))
          utils.emitNotif('success', $filter('i18n')('settings_saved', $rootScope.loc.ale, textObject))
      }
  
      var resetSettings = function () {
          var modalScope = $rootScope.$new()
  
          modalScope.title = $filter('i18n')('reset_confirm_title', $rootScope.loc.ale, textObject)
          modalScope.text = $filter('i18n')('reset_confirm_text', $rootScope.loc.ale, textObject)
          modalScope.submitText = $filter('i18n')('reset', $rootScope.loc.ale, textObjectCommon)
          modalScope.cancelText = $filter('i18n')('cancel', $rootScope.loc.ale, textObjectCommon)
          modalScope.showQuestionMarkIcon = true
          modalScope.switchColors = true
  
          modalScope.submit = function submit() {
              settings.resetSettings()
              utils.emitNotif('success', $filter('i18n')('settings_reset', $rootScope.loc.ale, textObject))
              modalScope.closeWindow()
          }
  
          modalScope.cancel = function cancel() {
              modalScope.closeWindow()
          }
  
          windowManagerService.getModal('modal_attention', modalScope)
      }
  
      var highlightsCount = function () {
          var character = Object.keys($scope.highlights.character).length
          var tribe = Object.keys($scope.highlights.tribe).length
          
          return character + tribe
      }
  
      var openProfile = function (type, itemId) {
          var handler = type === 'character'
              ? windowDisplayService.openCharacterProfile
              : windowDisplayService.openTribeProfile
  
          handler(itemId)
      }
  
      var eventHandlers = {
          addHighlightAutoCompleteSelect: function (item) {
              $scope.selectedHighlight = {
                  id: item.id,
                  type: item.type,
                  name: item.name
              }
          },
          highlightUpdate: function (event) {
              updateHighlightNames()
          },
          highlightAddErrorExists: function (event) {
              utils.emitNotif('error', $filter('i18n')('highlight_add_error_exists', $rootScope.loc.ale, textObject))
          },
          highlightAddErrorNoEntry: function (event) {
              utils.emitNotif('error', $filter('i18n')('highlight_add_error_no_entry', $rootScope.loc.ale, textObject))
          },
          highlightAddErrorInvalidColor: function (event) {
              utils.emitNotif('error', $filter('i18n')('highlight_add_error_invalid_color', $rootScope.loc.ale, textObject))
          },
          onMouseLeaveMinimap: function (event) {
              var event = new MouseEvent('mouseup', {
                  view: window,
                  bubbles: true,
                  cancelable: true
              })
  
              hideTooltip()
              $crossCanvas.dispatchEvent(event)
          },
          onMouseMoveMinimap: function (event) {
              hideTooltip()
              $crossCanvas.style.cursor = 'url(' + cdn.getPath('/img/cursor/grab_pushed.png') + '), move'
          },
          onMouseStopMoveMinimap: function (event) {
              $crossCanvas.style.cursor = ''
          }
      }
  
      var init = function () {
          var opener
  
          settings = minimap.getSettings()
          MapController = transferredSharedDataService.getSharedData('MapController')
          $minimapCanvas = document.createElement('canvas')
          $minimapCanvas.className = 'minimap'
          $crossCanvas = document.createElement('canvas')
          $crossCanvas.className = 'cross'
  
          minimap.setViewport($minimapCanvas)
          minimap.setCross($crossCanvas)
  
          tooltipWrapper = document.querySelector('#map-tooltip')
          windowWrapper = document.querySelector('#wrapper')
          mapWrapper = document.querySelector('#map')
  
          opener = new FrontButton('Minimap', {
              classHover: false,
              classBlur: false,
              onClick: function () {
                  var current = minimap.getMapPosition()
  
                  if (!current) {
                      return false
                  }
  
                  minimap.setCurrentPosition(current[0], current[1])
                  buildWindow()
              }
          })
  
          interfaceOverflow.addTemplate('twoverflow_minimap_window', `<div id="two-minimap" class="win-content two-window"><header class="win-head"><h2>Minimap</h2><ul class="list-btn"><li><a href="#" class="size-34x34 btn-red icon-26x26-close" ng-click="closeWindow()"></a></li></ul></header><div class="win-main small-select" scrollbar=""><div class="tabs tabs-bg"><div class="tabs-three-col"><div class="tab" ng-click="selectTab(TAB_TYPES.MINIMAP)" ng-class="{'tab-active': selectedTab == TAB_TYPES.MINIMAP}"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.MINIMAP}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.MINIMAP}">{{ 'minimap' | i18n:loc.ale:textObject }}</a></div></div></div><div class="tab" ng-click="selectTab(TAB_TYPES.HIGHLIGHTS)" ng-class="{'tab-active': selectedTab == TAB_TYPES.HIGHLIGHTS}"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.HIGHLIGHTS}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.HIGHLIGHTS}">{{ 'highlights' | i18n:loc.ale:textObject }}</a></div></div></div><div class="tab" ng-click="selectTab(TAB_TYPES.SETTINGS)" ng-class="{'tab-active': selectedTab == TAB_TYPES.SETTINGS}"><div class="tab-inner"><div ng-class="{'box-border-light': selectedTab === TAB_TYPES.SETTINGS}"><a href="#" ng-class="{'btn-icon btn-orange': selectedTab !== TAB_TYPES.SETTINGS}">{{ 'settings' | i18n:loc.ale:textObjectCommon }}</a></div></div></div></div></div><div ng-show="selectedTab === TAB_TYPES.MINIMAP" class="minimap-container"></div><div class="box-paper" ng-class="{'footer': selectedTab == TAB_TYPES.SETTINGS}"><div class="scroll-wrap"><div ng-show="selectedTab == TAB_TYPES.HIGHLIGHTS"><h5 class="twx-section">{{ 'add' | i18n:loc.ale:textObject }}</h5><table class="tbl-border-light tbl-striped add-highlight"><colgroup><col width="40%"><col><col width="4%"><col width="4%"></colgroup><tbody><tr><td><div auto-complete="autoComplete"></div></td><td class="text-center"><span ng-show="selectedHighlight" class="icon-26x26-rte-{{ selectedHighlight.type }}"></span> {{ selectedHighlight.name }}</td><td><div class="color-container box-border-dark" ng-click="openColorPalette('add_custom_highlight')" ng-style="{'background-color': addHighlightColor }" tooltip="" tooltip-content="{{ 'tooltip_pick_color' | i18n:loc.ale:textObject }}"></div></td><td><span class="btn-orange icon-26x26-plus" ng-click="addCustomHighlight()" tooltip="" tooltip-content="{{ 'add' | i18n:loc.ale:textObject }}"></span></td></tr></tbody></table><h5 class="twx-section">{{ TAB_TYPES.HIGHLIGHTS | i18n:loc.ale:textObject }}</h5><p class="text-center" ng-show="!highlightsCount()">{{ 'no_highlights' | i18n:loc.ale:textObject }}</p><table class="tbl-border-light tbl-striped" ng-show="highlightsCount()"><colgroup><col width="4%"><col><col width="4%"><col width="4%"></colgroup><tbody><tr ng-repeat="(id, color) in highlights.character"><td><span class="icon-26x26-rte-character"></span></td><td><span class="open-profile" ng-click="openProfile('character', id)">{{ highlightNames.character[id] }}</span></td><td><div class="color-container box-border-dark" ng-click="openColorPalette('edit_custom_highlight', 'character', id)" ng-style="{'background-color': color }"></div></td><td><a class="size-26x26 btn-red icon-20x20-close" ng-click="removeHighlight('character', id)" tooltip="" tooltip-content="{{ 'remove' | i18n:loc.ale:textObject }}"></a></td></tr><tr ng-repeat="(id, color) in highlights.tribe"><td><span class="icon-26x26-rte-tribe"></span></td><td><span class="open-profile" ng-click="openProfile('tribe', id)">{{ highlightNames.tribe[id] }}</span></td><td><div class="color-container box-border-dark" ng-click="openColorPalette('edit_custom_highlight', 'tribe', id)" ng-style="{'background-color': color }"></div></td><td><a class="size-26x26 btn-red icon-20x20-close" ng-click="removeHighlight('tribe', id)" tooltip="" tooltip-content="{{ 'remove' | i18n:loc.ale:textObject }}"></a></td></tr></tbody></table></div><div ng-show="selectedTab == TAB_TYPES.SETTINGS"><table class="tbl-border-light tbl-striped"><colgroup><col width="60%"><col><col width="56px"></colgroup><tbody><tr><th colspan="3">{{ 'misc' | i18n:loc.ale:textObject }}</th></tr><tr><td>{{ 'settings_right_click_action' | i18n:loc.ale:textObject }}</td><td colspan="3"><div select="" list="actionTypes" selected="settings[SETTINGS.RIGHT_CLICK_ACTION]" drop-down="true"></div></td></tr><tr><td colspan="2">{{ 'settings_show_cross' | i18n:loc.ale:textObject }}</td><td><div switch-slider="" value="settings[SETTINGS.SHOW_CROSS]" vertical="false" size="'56x28'" enabled="true"></div></td></tr><tr><td colspan="2">{{ 'settings_show_demarcations' | i18n:loc.ale:textObject }}</td><td><div switch-slider="" value="settings[SETTINGS.SHOW_DEMARCATIONS]" vertical="false" size="'56x28'" enabled="true"></div></td></tr><tr><td colspan="2">{{ 'settings_show_barbarians' | i18n:loc.ale:textObject }}</td><td><div switch-slider="" value="settings[SETTINGS.SHOW_BARBARIANS]" vertical="false" size="'56x28'" enabled="true"></div></td></tr><tr><td colspan="2">{{ 'settings_show_ghost_villages' | i18n:loc.ale:textObject }}</td><td><div switch-slider="" value="settings[SETTINGS.SHOW_GHOST_VILLAGES]" vertical="false" size="'56x28'" enabled="true"></div></td></tr><tr><td colspan="2">{{ 'settings_show_only_custom_highlights' | i18n:loc.ale:textObject }}</td><td><div switch-slider="" value="settings[SETTINGS.SHOW_ONLY_CUSTOM_HIGHLIGHTS]" vertical="false" size="'56x28'" enabled="true"></div></td></tr><tr><td colspan="2">{{ 'settings_highlight_own' | i18n:loc.ale:textObject }}</td><td><div switch-slider="" value="settings[SETTINGS.HIGHLIGHT_OWN]" vertical="false" size="'56x28'" enabled="true"></div></td></tr><tr><td colspan="2">{{ 'settings_highlight_selected' | i18n:loc.ale:textObject }}</td><td><div switch-slider="" value="settings[SETTINGS.HIGHLIGHT_SELECTED]" vertical="false" size="'56x28'" enabled="true"></div></td></tr><tr><td colspan="2">{{ 'settings_highlight_diplomacy' | i18n:loc.ale:textObject }}</td><td><div switch-slider="" value="settings[SETTINGS.HIGHLIGHT_DIPLOMACY]" vertical="false" size="'56x28'" enabled="true"></div></td></tr></tbody></table><table class="tbl-border-light tbl-striped"><colgroup><col><col width="29px"></colgroup><tbody><tr><th colspan="2">{{ 'colors_misc' | i18n:loc.ale:textObject }}</th></tr><tr><td>{{ 'settings_colors_background' | i18n:loc.ale:textObject }}</td><td><div class="color-container box-border-dark" ng-click="openColorPalette('setting', SETTINGS.COLOR_BACKGROUND)" ng-style="{'background-color': settings[SETTINGS.COLOR_BACKGROUND] }"></div></td></tr><tr><td>{{ 'settings_colors_province' | i18n:loc.ale:textObject }}</td><td><div class="color-container box-border-dark" ng-click="openColorPalette('setting', SETTINGS.COLOR_PROVINCE)" ng-style="{'background-color': settings[SETTINGS.COLOR_PROVINCE] }"></div></td></tr><tr><td>{{ 'settings_colors_continent' | i18n:loc.ale:textObject }}</td><td><div class="color-container box-border-dark" ng-click="openColorPalette('setting', SETTINGS.COLOR_CONTINENT)" ng-style="{'background-color': settings[SETTINGS.COLOR_CONTINENT] }"></div></td></tr><tr><td>{{ 'settings_colors_cross' | i18n:loc.ale:textObject }}</td><td><div class="color-container box-border-dark" ng-click="openColorPalette('setting', SETTINGS.COLOR_CROSS)" ng-style="{'background-color': settings[SETTINGS.COLOR_CROSS] }"></div></td></tr><tr><td>{{ 'settings_colors_quick_highlight' | i18n:loc.ale:textObject }}</td><td><div class="color-container box-border-dark" ng-click="openColorPalette('setting', SETTINGS.COLOR_QUICK_HIGHLIGHT)" ng-style="{'background-color': settings[SETTINGS.COLOR_QUICK_HIGHLIGHT] }"></div></td></tr><tr><td>{{ 'settings_colors_ghost' | i18n:loc.ale:textObject }}</td><td><div class="color-container box-border-dark" ng-click="openColorPalette('setting', SETTINGS.COLOR_GHOST)" ng-style="{'background-color': settings[SETTINGS.COLOR_GHOST] }"></div></td></tr></tbody></table></div></div></div></div><footer class="win-foot" ng-show="selectedTab === TAB_TYPES.SETTINGS"><ul class="list-btn list-center"><li><a href="#" class="btn-border btn-red" ng-click="resetSettings()">{{ 'reset' | i18n:loc.ale:textObjectCommon }}</a></li><li><a href="#" class="btn-border btn-green" ng-click="saveSettings()">{{ 'save' | i18n:loc.ale:textObjectCommon }}</a></li></ul></footer></div>`)
          interfaceOverflow.addStyle('#map-tooltip{z-index:1000}#two-minimap .minimap{position:absolute;left:0;top:38px;z-index:5}#two-minimap .cross{position:absolute;left:0;top:38px;z-index:6}#two-minimap .box-paper:not(.footer) .scroll-wrap{margin-bottom:40px}#two-minimap span.select-wrapper{width:100%}#two-minimap .add-highlight input{width:100%}#two-minimap .open-profile:hover{font-weight:bold;text-shadow:-1px 1px 0 #e0cc97}')
      }
  
      var buildWindow = function () {
          $scope = $rootScope.$new()
          $scope.textObject = textObject
          $scope.textObjectCommon = textObjectCommon
          $scope.SETTINGS = SETTINGS
          $scope.TAB_TYPES = TAB_TYPES
          $scope.selectedTab = DEFAULT_TAB
          $scope.selectedHighlight = false
          $scope.addHighlightColor = '#000000'
          $scope.highlights = minimap.getHighlights()
          $scope.highlightNames = highlightNames
          $scope.actionTypes = Settings.encodeList(ACTION_TYPES, {
              textObject: textObject,
              disabled: false
          })
          $scope.autoComplete = {
              type: ['character', 'tribe', 'village'],
              placeholder: $filter('i18n')('placeholder_search', $rootScope.loc.ale, textObject),
              onEnter: eventHandlers.addHighlightAutoCompleteSelect
          }
  
          // functions
          $scope.selectTab = selectTab
          $scope.openColorPalette = openColorPalette
          $scope.addCustomHighlight = addCustomHighlight
          $scope.removeHighlight = minimap.removeHighlight
          $scope.saveSettings = saveSettings
          $scope.resetSettings = resetSettings
          $scope.highlightsCount = highlightsCount
          $scope.openProfile = openProfile
  
          settings.injectSettings($scope, {
              textObject: textObject
          })
  
          eventScope = new EventScope('twoverflow_minimap_window')
          eventScope.register(eventTypeProvider.GROUPS_VILLAGES_CHANGED, eventHandlers.highlightUpdate, true)
          eventScope.register(eventTypeProvider.MINIMAP_HIGHLIGHT_ADD_ERROR_EXISTS, eventHandlers.highlightAddErrorExists)
          eventScope.register(eventTypeProvider.MINIMAP_HIGHLIGHT_ADD_ERROR_NO_ENTRY, eventHandlers.highlightAddErrorNoEntry)
          eventScope.register(eventTypeProvider.MINIMAP_HIGHLIGHT_ADD_ERROR_INVALID_COLOR, eventHandlers.highlightAddErrorInvalidColor)
          eventScope.register(eventTypeProvider.MINIMAP_VILLAGE_HOVER, showTooltip)
          eventScope.register(eventTypeProvider.MINIMAP_VILLAGE_BLUR, hideTooltip)
          eventScope.register(eventTypeProvider.MINIMAP_MOUSE_LEAVE, eventHandlers.onMouseLeaveMinimap)
          eventScope.register(eventTypeProvider.MINIMAP_START_MOVE, eventHandlers.onMouseMoveMinimap)
          eventScope.register(eventTypeProvider.MINIMAP_STOP_MOVE, eventHandlers.onMouseStopMoveMinimap)
  
          windowManagerService.getScreenWithInjectedScope('!twoverflow_minimap_window', $scope)
          updateHighlightNames()
          appendCanvas()
          minimap.enableRendering()
      }
  
      return init
  })
  
  define('two/minimap/settingsMap', [
      'two/minimap/settings',
      'two/minimap/actionTypes'
  ], function (
      SETTINGS,
      ACTION_TYPES
  ) {
      return {
          [SETTINGS.RIGHT_CLICK_ACTION]: {
              default: ACTION_TYPES.HIGHLIGHT_PLAYER,
              inputType: 'select',
              disabledOption: false,
              update: false
          },
          // [SETTINGS.FLOATING_MINIMAP]: {
          //     default: false,
          //     inputType: 'checkbox',
          //     update: false
          // }
          [SETTINGS.SHOW_CROSS]: {
              default: true,
              inputType: 'checkbox',
              update: true
          },
          [SETTINGS.SHOW_DEMARCATIONS]: {
              default: true,
              inputType: 'checkbox',
              update: true
          },
          [SETTINGS.SHOW_BARBARIANS]: {
              default: false,
              inputType: 'checkbox',
              update: true
          },
          [SETTINGS.SHOW_GHOST_VILLAGES]: {
              default: false,
              inputType: 'checkbox',
              update: true
          },
          [SETTINGS.SHOW_ONLY_CUSTOM_HIGHLIGHTS]: {
              default: false,
              inputType: 'checkbox',
              update: true
          },
          [SETTINGS.HIGHLIGHT_OWN]: {
              default: true,
              inputType: 'checkbox',
              update: true
          },
          [SETTINGS.HIGHLIGHT_SELECTED]: {
              default: true,
              inputType: 'checkbox',
              update: true
          },
          [SETTINGS.HIGHLIGHT_DIPLOMACY]: {
              default: true,
              inputType: 'checkbox',
              update: true
          },
          [SETTINGS.COLOR_SELECTED]: {
              default: '#ffffff',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_BARBARIAN]: {
              default: '#969696',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_PLAYER]: {
              default: '#f0c800',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_QUICK_HIGHLIGHT]: {
              default: '#ffffff',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_BACKGROUND]: {
              default: '#436213',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_PROVINCE]: {
              default: '#ffffff',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_CONTINENT]: {
              default: '#cccccc',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_CROSS]: {
              default: '#999999',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_TRIBE]: {
              default: '#0000DB',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_ALLY]: {
              default: '#00a0f4',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_ENEMY]: {
              default: '#ED1212',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_FRIENDLY]: {
              default: '#BF4DA4',
              inputType: 'color',
              update: true
          },
          [SETTINGS.COLOR_GHOST]: {
              default: '#3E551C',
              inputType: 'color',
              update: true
          }
      }
  })
  
  define('two/minimap/settings', [], function () {
      return {
          RIGHT_CLICK_ACTION: 'right_click_action',
          FLOATING_MINIMAP: 'floating_minimap',
          SHOW_CROSS: 'show_cross',
          SHOW_DEMARCATIONS: 'show_demarcations',
          SHOW_BARBARIANS: 'show_barbarians',
          SHOW_GHOST_VILLAGES: 'show_ghost_villages',
          SHOW_ONLY_CUSTOM_HIGHLIGHTS: 'show_only_custom_highlights',
          HIGHLIGHT_OWN: 'highlight_own',
          HIGHLIGHT_SELECTED: 'highlight_selected',
          HIGHLIGHT_DIPLOMACY: 'highlight_diplomacy',
          COLOR_GHOST: 'color_ghost',
          COLOR_QUICK_HIGHLIGHT: 'color_quick_highlight',
          COLOR_BACKGROUND: 'color_background',
          COLOR_PROVINCE: 'color_province',
          COLOR_CONTINENT: 'color_continent',
          COLOR_CROSS: 'color_cross'
      }
  })
  
  require([
      'two/language',
      'two/ready',
      'two/minimap',
      'two/minimap/ui',
      'two/minimap/events',
      'two/minimap/actionTypes',
      'two/minimap/settings',
      'two/minimap/settingsMap',
  ], function (
      twoLanguage,
      ready,
      minimap,
      minimapInterface
  ) {
      if (minimap.initialized) {
          return false
      }
  
      ready(function () {
          twoLanguage.add('minimap', {"cs_cz":{"minimap":{"minimap":"Minimap","highlights":"Highlights","add":"Add highlight","remove":"Remove highlight","placeholder_search":"Search player/tribe","highlight_add_success":"Highlight added","highlight_add_error":"Specify a highlight first","highlight_update_success":"Highlight updated","highlight_remove_success":"Highlight removed","highlight_villages":"Villages","highlight_players":"Players","highlight_tribes":"Tribes","highlight_add_error_exists":"Highlight already exists!","highlight_add_error_no_entry":"Select a player/tribe first!","highlight_add_error_invalid_color":"Invalid color!","village":"Village","player":"Player","tribe":"Tribe","color":"Color (Hex)","tooltip_pick_color":"Select a color","misc":"Miscellaneous settings","colors_misc":"Miscellaneous colors","colors_diplomacy":"Diplomacy colors","settings_saved":"Settings saved!","settings_right_click_action":"Village's right click action","highlight_village":"Highlight village","highlight_player":"Highlight player","highlight_tribe":"Highlight tribe","settings_show_floating_minimap":"Show floating minimap","settings_show_cross":"Show position cross","settings_show_demarcations":"Show province/continent demarcations","settings_show_barbarians":"Show barbarian villages","settings_show_ghost_villages":"Show non-loaded villages","settings_show_only_custom_highlights":"Show only custom highlights","settings_highlight_own":"Highlight own villages","settings_highlight_selected":"highlight selected village","settings_highlight_diplomacy":"Auto highlight tribe diplomacies","settings_colors_background":"Minimap background","settings_colors_province":"Province demarcation","settings_colors_continent":"Continent demarcation","settings_colors_quick_highlight":"Quick highlight","settings_colors_tribe":"Own tribe","settings_colors_player":"Own villages","settings_colors_selected":"Selected village","settings_colors_ghost":"Non-loaded villages","settings_colors_ally":"Ally","settings_colors_pna":"PNA","settings_colors_enemy":"Enemy","settings_colors_other":"Other","settings_colors_barbarian":"Barbarian","settings_colors_cross":"Position cross","settings_reset":"Settings reseted","tooltip_village":"Village","tooltip_village_points":"Village points","tooltip_player":"Player name","tooltip_player_points":"Player points","tooltip_tribe":"Tribe","tooltip_tribe_points":"Tribe points","tooltip_province":"Province name","no_highlights":"No highlights created","reset_confirm_title":"Reset settings","reset_confirm_text":"All settings gonna be reseted to the default settings.","reset_confirm_highlights_text":"Also, all highlights are going to be deleted."}},"de_de":{"minimap":{"minimap":"Minimap","highlights":"Highlights","add":"Highlight hinzufügen","remove":"Highlight entfernen","placeholder_search":"Dorf/Spieler/Stamm","highlight_add_success":"Highlight hinzugefügt","highlight_add_error":"Zuerst ein Highlight bestimmen","highlight_update_success":"Highlight aktualisiert","highlight_remove_success":"Highlight entfernt","highlight_villages":"Dörfer","highlight_players":"Spieler","highlight_tribes":"Stämme","highlight_add_error_exists":"Highlight existiert schon!","highlight_add_error_no_entry":"Zuerst ein Dorf/Spieler/Stamm wählen!","highlight_add_error_invalid_color":"Ungültige Farbe!","village":"Dorf","player":"Spieler","tribe":"Stamm","color":"Wert (Hex)","tooltip_pick_color":"Eine Farbe auswählen","misc":"Verschiedenes einstellungen","colors_misc":"Sonstige Farben","colors_diplomacy":"Diplomatie Farben","settings_saved":"Einstellungen gespeichert!","settings_right_click_action":"Rechtsklickoption des Dorfes","highlight_village":"Dorf hervorheben","highlight_player":"Spieler hervorheben","highlight_tribe":"Stamm hervorheben","settings_show_floating_minimap":"Zeige Minimap","settings_show_cross":"Positionskreuz anzeigen","settings_show_demarcations":"Zeige Provinz/Kontinent Grenzen","settings_show_barbarians":"Zeige Barbarendörfer","settings_show_ghost_villages":"Zeige ungeladene Dörfer","settings_show_only_custom_highlights":"Nur benutzerdefinierte Markierungen anzeigen","settings_highlight_own":"Markieren Sie eigene Dörfer automatisch","settings_highlight_selected":"Markieren Sie das ausgewählte Dorf automatisch","settings_highlight_diplomacy":"Stammesdiplomatie automatisch hervorheben","settings_colors_background":"Minimap Hintergrund","settings_colors_province":"Provinzgrenzen","settings_colors_continent":"Kontinentgrenzen","settings_colors_quick_highlight":"Schnelles Highlight","settings_colors_tribe":"Eigener stamm","settings_colors_player":"Eigene Dörfer","settings_colors_selected":"Ausgewähltes Dorf","settings_colors_ghost":"Ungeladene Dörfer","settings_colors_ally":"Verbündeter","settings_colors_pna":"NAP","settings_colors_enemy":"Feind","settings_colors_other":"Sonstige","settings_colors_barbarian":"Barbaren","settings_colors_cross":"Kreuz positionieren","settings_reset":"Einstellungen wurden zurückgesetzt","tooltip_village":"Dorf","tooltip_village_points":"Dorfpunkte","tooltip_player":"Spielername","tooltip_player_points":"Spielerpunkte","tooltip_tribe":"Stamm","tooltip_tribe_points":"Stammpunkte","tooltip_province":"Provinzname","no_highlights":"Kein markieren erstellt","reset_confirm_title":"Protokolle zurückgesetzt","reset_confirm_text":"All settings gonna be reseted to the default settings.","reset_confirm_highlights_text":"Also, all highlights are going to be deleted."}},"en_us":{"minimap":{"minimap":"Minimap","highlights":"Highlights","add":"Add highlight","remove":"Remove highlight","placeholder_search":"Search player/tribe","highlight_add_success":"Highlight added","highlight_add_error":"Specify a highlight first","highlight_update_success":"Highlight updated","highlight_remove_success":"Highlight removed","highlight_villages":"Villages","highlight_players":"Players","highlight_tribes":"Tribes","highlight_add_error_exists":"Highlight already exists!","highlight_add_error_no_entry":"Select a player/tribe first!","highlight_add_error_invalid_color":"Invalid color!","village":"Village","player":"Player","tribe":"Tribe","color":"Color (Hex)","tooltip_pick_color":"Select a color","misc":"Miscellaneous settings","colors_misc":"Miscellaneous colors","colors_diplomacy":"Diplomacy colors","settings_saved":"Settings saved!","settings_right_click_action":"Village's right click action","highlight_village":"Highlight village","highlight_player":"Highlight player","highlight_tribe":"Highlight tribe","settings_show_floating_minimap":"Show floating minimap","settings_show_cross":"Show position cross","settings_show_demarcations":"Show province/continent demarcations","settings_show_barbarians":"Show barbarian villages","settings_show_ghost_villages":"Show non-loaded villages","settings_show_only_custom_highlights":"Show only custom highlights","settings_highlight_own":"Highlight own villages","settings_highlight_selected":"highlight selected village","settings_highlight_diplomacy":"Auto highlight tribe diplomacies","settings_colors_background":"Minimap background","settings_colors_province":"Province demarcation","settings_colors_continent":"Continent demarcation","settings_colors_quick_highlight":"Quick highlight","settings_colors_tribe":"Own tribe","settings_colors_player":"Own villages","settings_colors_selected":"Selected village","settings_colors_ghost":"Non-loaded villages","settings_colors_ally":"Ally","settings_colors_pna":"PNA","settings_colors_enemy":"Enemy","settings_colors_other":"Other","settings_colors_barbarian":"Barbarian","settings_colors_cross":"Position cross","settings_reset":"Settings reseted","tooltip_village":"Village","tooltip_village_points":"Village points","tooltip_player":"Player name","tooltip_player_points":"Player points","tooltip_tribe":"Tribe","tooltip_tribe_points":"Tribe points","tooltip_province":"Province name","no_highlights":"No highlights created","reset_confirm_title":"Reset settings","reset_confirm_text":"All settings gonna be reseted to the default settings.","reset_confirm_highlights_text":"Also, all highlights are going to be deleted."}},"pl_pl":{"minimap":{"minimap":"Minimap","highlights":"Podświetlenie","add":"Dodaj podświetlenie","remove":"Usuń podświetlenie","placeholder_search":"Szukaj gracz/plemie","highlight_add_success":"Podświetlenie dodane","highlight_add_error":"Najpierw sprecyzuj podświetlenie","highlight_update_success":"Podświetlenie zaktualizowane","highlight_remove_success":"Podświetlenie usunięte","highlight_villages":"Wioski","highlight_players":"Gracze","highlight_tribes":"Plemiona","highlight_add_error_exists":"Podświetlenie już istnieje!","highlight_add_error_no_entry":"Najpierw wybierz gracza/plemię!","highlight_add_error_invalid_color":"Nieprawidłowy kolor!","village":"Wioska","player":"Gracz","tribe":"Plemię","color":"Kolor (Hex)","tooltip_pick_color":"Wybierz kolor","misc":"Pozostałe ustawienia","colors_misc":"Różne kolory","colors_diplomacy":"Dyplomacja - kolory","settings_saved":"Ustawienia zapisane!","settings_right_click_action":"PPM aby wykonać działanie na wiosce","highlight_village":"Podświetl wioskę","highlight_player":"Podświetl gracza","highlight_tribe":"Podświetl plemie","settings_show_floating_minimap":"Pokaż ruchomą mapę","settings_show_cross":"Pokaż krzyż pozycjonowania","settings_show_demarcations":"Pokaż granice prowincji/kontynentów","settings_show_barbarians":"Pokaż wioski barbarzyńskie","settings_show_ghost_villages":"Pokaż niezaładowane wioski","settings_show_only_custom_highlights":"Pokaż tylko własne podświetlenia","settings_highlight_own":"Podświetl własne wioski","settings_highlight_selected":"Podświetl wybraną wioskę","settings_highlight_diplomacy":"Automatycznie podświetl plemienną dyplomację","settings_colors_background":"Tło minimapy","settings_colors_province":"Granica prowincji","settings_colors_continent":"Granica kontynentu","settings_colors_quick_highlight":"Szybkie podświetlenie","settings_colors_tribe":"Własne plemie","settings_colors_player":"Własne wioski","settings_colors_selected":"Wybrana wioska","settings_colors_ghost":"Niezaładowana wioska","settings_colors_ally":"Sojusznik","settings_colors_pna":"PON","settings_colors_enemy":"Wróg","settings_colors_other":"Pozostałe wioski graczy","settings_colors_barbarian":"Wioski barbarzyńskie","settings_colors_cross":"Krzyż pozycjonowania","settings_reset":"Ustawienia zresetowane","tooltip_village":"Wioska","tooltip_village_points":"Punkty wioski","tooltip_player":"Nazwa gracza","tooltip_player_points":"Punkty gracza","tooltip_tribe":"Plemię","tooltip_tribe_points":"Punkty plemienia","tooltip_province":"Prowincja","no_highlights":"Brak utworzonych podświetleń","reset_confirm_title":"Resetuj ustawienia","reset_confirm_text":"Wszystkie ustawienia zostaną przywrócone do domyślnych.","reset_confirm_highlights_text":"Jak również wszystkie podświetlenia zostaną usunięte."}},"pt_br":{"minimap":{"minimap":"Minimapa","highlights":"Marcações","add":"Adicionar marcação","remove":"Remover marcação","placeholder_search":"Procurar jogador/tribo","highlight_add_success":"Marcação adicionada","highlight_add_error":"Especifique uma marcação primeiro","highlight_update_success":"Marcação atualizada","highlight_remove_success":"Marcação removida","highlight_villages":"Aldeias","highlight_players":"Jogadores","highlight_tribes":"Tribos","highlight_add_error_exists":"Marcação já existe!","highlight_add_error_no_entry":"Selecione uma jogador/tribo primeiro!","highlight_add_error_invalid_color":"Cor inválida!","village":"Aldeia","player":"Jogador","tribe":"Tribo","color":"Cor (Hex)","tooltip_pick_color":"Selecione uma cor","misc":"Configurações diversas","colors_misc":"Cores diversas","colors_diplomacy":"Cores da diplomacia","settings_saved":"Configurações salvas!","settings_right_click_action":"Ação de clique direito na aldeia","highlight_village":"Marcar aldeia","highlight_player":"Marcar jogador","highlight_tribe":"Marcar tribo","settings_show_floating_minimap":"Mostrar minimapa flutuante","settings_show_cross":"Mostrar marcação da posição atual","settings_show_demarcations":"Mostrar demarcações das provincias/continentes","settings_show_barbarians":"Mostrar aldeias bárbaras","settings_show_ghost_villages":"Mostrar aldeias não carregadas","settings_show_only_custom_highlights":"Mostrar apenas marcações manuais","settings_highlight_own":"Marcar próprias aldeias","settings_highlight_selected":"Marcar aldeia selecionada","settings_highlight_diplomacy":"Marcação automática baseado na diplomacia","settings_colors_background":"Fundo do minimapa","settings_colors_province":"Demarcação da provincia","settings_colors_continent":"Demarcação do continente","settings_colors_quick_highlight":"Marcação rápida","settings_colors_tribe":"Própria tribo","settings_colors_player":"Aldeias próprias","settings_colors_selected":"Aldeia selecionada","settings_colors_ghost":"Aldeias não carregadas","settings_colors_ally":"Aliados","settings_colors_pna":"PNA","settings_colors_enemy":"Inimigos","settings_colors_other":"Outros","settings_colors_barbarian":"Aldeias Bárbaras","settings_colors_cross":"Cruz da posição","settings_reset":"Configurações resetadas","tooltip_village":"Aldeia","tooltip_village_points":"Pontos da aldeia","tooltip_player":"Nome do jogador","tooltip_player_points":"Pontos do jogador","tooltip_tribe":"Tribo","tooltip_tribe_points":"Pontos da tribo","tooltip_province":"Nome da província","no_highlights":"Nenhuma marcação criada","reset_confirm_title":"Resetar configurações","reset_confirm_text":"Todas as configurações serão resetas para as configurações padrões.","reset_confirm_highlights_text":"Todas marcações também serão deletadas"}}})
          minimap.init()
          minimapInterface()
          minimap.run()
      })
  })
  
  define('two/ui', [
      'conf/conf',
      'conf/cdn'
  ], function (
      conf,
      cdnConf
  ) {
      var interfaceOverflow = {}
      var templates = {}
  
      var $head = document.querySelector('head')
      var httpService = injector.get('httpService')
      var templateManagerService = injector.get('templateManagerService')
      var $templateCache = injector.get('$templateCache')
  
      templateManagerService.load = function (templateName, onSuccess, opt_onError) {
          var path
  
          var success = function (data, status, headers, config) {
              $templateCache.put(path.substr(1), data)
  
              if (angular.isFunction(onSuccess)) {
                  onSuccess(data, status, headers, config)
              }
  
              if (!$rootScope.$$phase) {
                  $rootScope.$apply()
              }
          }
          var error = function (data, status, headers, config) {
              if (angular.isFunction(opt_onError)) {
                  opt_onError(data, status, headers, config)
              }
          }
  
          if (0 !== templateName.indexOf('!')) {
              path = conf.TEMPLATE_PATH_EXT.join(templateName)
          } else {
              path = templateName.substr(1)
          }
  
          if ($templateCache.get(path.substr(1))) {
              success($templateCache.get(path.substr(1)), 304)
          } else {
              if (cdnConf.versionMap[path]) {
                  httpService.get(path, success, error)
              } else {
                  success(templates[path], 304)
              }
          }
      }
  
      interfaceOverflow.addTemplate = function (path, data) {
          templates[path] = data
      }
  
      interfaceOverflow.addStyle = function (styles) {
          var $style = document.createElement('style')
          $style.type = 'text/css'
          $style.appendChild(document.createTextNode(styles))
          $head.appendChild($style)
      }
  
      return interfaceOverflow
  })
  
  define('two/ui/button', [], function () {
      function FrontButton (label, options) {
          this.options = options = angular.merge({
              label: label,
              className: '',
              classHover: 'expand-button',
              classBlur: 'contract-button',
              tooltip: false,
              onClick: function() {}
          }, options)
  
          this.buildWrapper()
          this.appendButton()
  
          var $elem = this.$elem
  
          var $label = $elem.querySelector('.label')
          var $quick = $elem.querySelector('.quickview')
  
          if (options.classHover) {
              $elem.addEventListener('mouseenter', function () {
                  $elem.classList.add(options.classHover)
                  $elem.classList.remove(options.classBlur)
  
                  // $label.hide()
                  $label.style.display = 'none'
                  // $quick.show()
                  $quick.style.display = ''
              })
          }
  
          if (options.classBlur) {
              $elem.addEventListener('mouseleave', function () {
                  $elem.classList.add(options.classBlur)
                  $elem.classList.remove(options.classHover)
  
                  // $quick.hide()
                  $quick.style.display = 'none'
                  // $label.show()
                  $label.style.display = ''
              })
          }
  
          if (options.tooltip) {
              $elem.addEventListener('mouseenter', function (event) {
                  $rootScope.$broadcast(
                      eventTypeProvider.TOOLTIP_SHOW,
                      'twoverflow-tooltip',
                      options.tooltip,
                      true,
                      event
                  )
              })
  
              $elem.addEventListener('mouseleave', function () {
                  $rootScope.$broadcast(eventTypeProvider.TOOLTIP_HIDE, 'twoverflow-tooltip')
              })
          }
  
          if (options.onClick) {
              $elem.addEventListener('click', options.onClick)
          }
  
          return this
      }
  
      FrontButton.prototype.updateQuickview = function (text) {
          this.$elem.querySelector('.quickview').innerHTML = text
      }
  
      FrontButton.prototype.hover = function (handler) {
          this.$elem.addEventListener('mouseenter', handler)
      }
  
      FrontButton.prototype.click = function (handler) {
          this.$elem.addEventListener('click', handler)
      }
  
      FrontButton.prototype.buildWrapper = function () {
          var $wrapper = document.getElementById('two-bar')
  
          if (!$wrapper) {
              $wrapper = document.createElement('div')
              $wrapper.id = 'two-bar'
              document.querySelector('#wrapper').appendChild($wrapper)
          }
  
          this.$wrapper = $wrapper
      }
  
      FrontButton.prototype.appendButton = function () {
          var $container = document.createElement('div')
          $container.innerHTML = '<div class="btn-border btn-green button ' + this.options.className + '"><div class="top-left"></div><div class="top-right"></div><div class="middle-top"></div><div class="middle-bottom"></div><div class="middle-left"></div><div class="middle-right"></div><div class="bottom-left"></div><div class="bottom-right"></div><div class="label">' + this.options.label + '</div><div class="quickview"></div></div>'
          var $elem = $container.children[0]
  
          this.$wrapper.appendChild($elem)
          this.$elem = $elem
      }
  
      FrontButton.prototype.destroy = function () {
          this.$elem.remove()
      }
  
      return FrontButton
  })
  
  require([
      'two/ui'
  ], function (
      interfaceOverflow
  ) {
      interfaceOverflow.addStyle('.two-window a.select-handler{-webkit-box-shadow:none;box-shadow:none}.two-window .small-select a.select-handler{height:22px;line-height:22px}.two-window .small-select a.select-button{height:22px}.two-window input::placeholder{color:rgba(255,243,208,0.7)}.two-window .green{color:#07770b}.two-window .red{color:#770707}.two-window .blue{color:#074677}#toolbar-left{height:calc(100% - 390px) !important;top:275px !important}#two-bar{position:absolute;top:105px;left:5px;margin-bottom:7px;z-index:10;width:100px}#two-bar .button{white-space:nowrap;position:relative;top:-17px;left:0px;min-width:70px;height:24px;padding:0 3px}#two-bar .label,#two-bar .quickview{margin:5px 0;font-size:12px}#two-bar .quickview{display:none}#wrapper.window-open #two-bar{left:719px}#wrapper.window-open.window-fullsize #two-bar{display:none}')
  })
  
  })(this)
  HgYpf1+cKSazfsnHpnve2rVqYTg2xdvMrPL76JWKNNSxesYB1LyyDiQQ9fWkCmhxzkRuLZTcpVC1lOU4eEDNPDUzitJVc6eUDn6zuSAwl2PDGLqrnx9ECPob6kkxaPiLBEK1LniIaFVz/c4SAJsf6U2ZaEfZwxMOYuaVCJTWypKz68LXV7y6sigWf7thMdfMKkMOgryA2r5pYYwWBaA3FzBhFM8uiRXFOnumn/jGt0SjYl8t+MWzbFABkxSFSdkTTE3F3zkDyBnptw/2J5VMXpwq1gfT1AQ4eOIyi1AHw5II5hCp80bIjmhSHyEyP7Ak0AcFwuIKR/vy/PLVv7156T/1M4u8e9n/1HXqNRnNzjMS9AuGQBlMfF5zxKoA6U2hph5xp0nv+ErX1KVqfXctbH+yk65tOAOa1tolNm56TjIyFNVpmIl8GwBMEHnSzKkuUJUHh8vAYcihMIFQi3hAHZ4T65hq27dyKkbGI1uqS7a/mXO8F+gZGuDZ0j4nClFsU1adj2wrgyq5KTlOlwTOJ8STApVO/Y2VGAJgwSgBEa3VsfzXZZJKLvxyjWC7z8+G3CQf9+FS13nG9ueEwEUBRqmywEfrAvWLF4rqq5fmiwCvcIjuqYCTu8v5nnXQd7+bgoZ/48dduXF8F4ZpaNj0/j60bgly+YLTeNMyUYosxPUhONaBUpeq3K7G7T/Ym2pfWh5ZU1MzBX/0XV/64iVYe4+jR3QD4aqeGaWdylPNjABw9upv9X3R+9GVXwsjmrZQCiJDjOI4scjnTyZZc0ZhKJmM9PcNYlsu4CLJjez3jt65ij45jpZPYhVG8SRNFrcQc7eeZ9evIl9xI96Xh4yqAAaXoJCOW3zuRGjfNwbRob6wNbkkYxTizaDx9B0+pY93rnWdTYxPf+xQ9p0yvCRPciEtJqFpKEfZwyXaupArOYLbM+JK2lS3HDhyRbgwanO6eoPvEaWLxOixLY+WOrrP5onUI4Z2TdMeQZgtYySaGrM6VJVFfmnRjsiwHXEG8KR5p2/fpxjWv7jpyyCd7JxR8v03nY0Fidt2H+z1dcz1LFx7xlctb2gHO9wz1+CS1L2tZSabD4f+Asx7g+a0JbYJJg6lgAPgHUh4QWRIJr4EAAAAASUVORK5CYII=")}#two-attack-view .icon-20x20-spy{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAN9SURBVDhPrZVLbBtVFIb/mTt+xC/GdlzHSZw2jhMIIbRENG5JsVFbFiAWSOy6SlnwqlggFgiJBQuW7BArNiwBCSGxYFGhSpRWTlrHSUxCGjt1Yzt+JX5nJuPHPMgMTqWqadpF/s3VPfecT0c6j0u98/FnOE7R3fPYpGX414/fU6IosoSmgxSFFxUF/0qyfJNhmFpo9prS9X0mkbGz53E/ErYzhP7oq8+v/nDlYt8lo8UcWEsUhX1oPBNbaJ06M911f7o04EZkbuL1SfcXV4LEMzrmQQ9N7C7XwFuR1U09oWBMRufq6dgC/yxgDfhgcc7vYQ1Xh57bta4meLhffhesvolLr7rOsw5boFYT3HWumUrH7uxDA51u7KHSgJnlCFFE8TQtSs+XdygwrQQuvCLDP+yBiwh2M+kJ5MrcVJ1vJdOxu/mjoBowu3yXqwriYrWpm9LpFO+gsYxensVWo4FIkiiCuZ/y9du9PG0OFvPb1tTSnWIuFqkOnZmWu5yH0oDqg+qw0xCiHZoJDjuoXomu4Pa6ghfenKUk2oh0Ng+vVWEnRwdCDUk3WqlUCodlqwFVqdDNxflmnWvZ+mxsqLTbwg4ZRJsrY/NBEp9MOzFItWFs8xge8fiILEykilxChaaWIuJBwR4CVeVjC6IMeXUtzwku1hZSKIJ0Jo/LLhH9nl6YbRboJAmdWo1ye8yeTrtzOVsW0pIkbx201yNANUu1PQB5baO4R044We2x3yhDEQSwp9owG90wujpwiSZ4LAar2USfu1/kFVGSVnxT55qPjZ46GYQwRQNDvpv/J/lT14xUqYHo3zVUxLx2X0kVEE4WYHdYB0bGxz9UoLyk2g+dZRXKUFRGlqRvc7lCmJcKmr2x14RUoPHH9QzWOREFyxgcF97HG2+/d3J/bL2qzxOXw8zsNdGgJ2vZMvf1UsEQVaENQcTP8/fAEYKKyQ/aPoSWbQAGcRdQ0KvGHbltQrOf7hFC37q1Uvzy12X5Xq21jXa5AYPVgR6TCTNnJzHu6sHmVjb+2vSET4156vpSoQbC3Mhu1z+4uSGH9U4bTozPIBC8CN+IH9bGMqqplXjfyZG46v9IlZ8ktfr7LZWr8q3bvIgpp77ppZWOkluPUNd//y2q6CxLf94If/NY2xyl/1sqUqpyQtzhZF2FfDZRKpXibv/pX1TYwd485i8A+A/DgaisICBxKQAAAABJRU5ErkJggg==")}#two-attack-view .icon-20x20-withdraw{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAQRSURBVDhPlZRtTBxFGMf/s7sc3AsHnBwHR6GHwcJBxLbQ1lYTFCsJidb4Qf2gxEox0WihEL+YmH6oxpjUtJCWJlarrUZj/GBjfaGlqSmvhRZqQQOIpdxxlAKF4+DeWO52x51luUKiqf6SzTzPf2f+OzP7zJBn3qzD/6H1i+M5kiztBkhn2b6aQU2OwWntf6akMPuFhqbjJzOt5jc0aR3/ath6uol0nm4StFTJjxlaTjYUFz/9fFWaPRuyTF/89VRjuvY6xjpDthw2iMVUlveEJfkyM7r0WWMKBblW9059L09p3tGD9Sgqyrdl5TjOqAPXsM4wStGk0wlfMmNw5ENK8djSstwAjv86PiHBac/fjJEbnaBm+0r/gDebfUxNNGKGlz8/9pyyjIpIlBZEZPoVazda4mA2G6uZbrFtIAPdbWrfQ4ePYCEcBR8V8wWeW7eXMcNlSS7VQhiN+l2stVtMaKwvJSz2Tk/Qm72tLETrL2eRpBewIa8Q6cl6pypqqJvO9o1wfF1V+UN4wGanfj6FSAEvpMU7qCjPQEvBKzjwsYv8MdxHDXeDJMX6O+KNZkizLozPBl15qtUKqiHHwWA06ekHBx8nRnOQcCabomYpzyPwTwawc3sKzh7JxatvT5LJII9QKMSGIeydhk7gwmqioS6ZI+QvixD56faYB0NXhuBq74HPNYaQVwdD2oOqaXryIL4/UYqXdtlp36AHd9wuSEYLIpLUoTppcKzelL8pgo/zMcGYoEdCigm8zqp2EBd9SLSbMNLvxXuHOtDe7yF5qQQF6TyyM20QON6idtTgy1+vgWegT5z1ixW51qTiieFbcHsWMDoaQlfXKNJTjfjz6ggq3+3ArXAy/BEO2fwCwkER4UUvxufCA44tO2Kz5Ddt2wnH5u0Yu96z7dJv00Jzf+jMz72B0vPXpgZFUfKkxkcyXn7/6rRflMfjdILVKs/BEF6CFBYxvSRjZnH5XM7WHT2a372yIYReFAgO6+K4jziONEclOm9zPnrquw6/8pbOKflAiTMLoz6ZXrgtNruSctyZm5xQatax4rCCasj2cXf1gfYnqvb/ULp3f0iSpGGmz5sdJ1q6h4+yU8JyVsyBQJjwoJ8EZybOMY3juAzWrqIalu59i6qZhsDhPAdZPacsFpfEmSyLPlKypYialPKSqZw2ObvYbXE8DKargzRiS15L2b7aFuX5VEsRleSbU77w0NwST9JSE4kMrkwptY1x+kStxz3+0XAtPM91ECo/K1PqDt51KxdC4Ioy+zqWj1xvQ4azuHL1hmLc15Dt6VPVtfNZqYlun6gcrOjyrDL7qSerar7p6r7xrcFggMCTrau3zn0NV8nd81pn28ULhVN+qVaToOe5SqYp4Y8JgqCUA/A3Zu24cT81pCsAAAAASUVORK5CYII=")}#two-attack-view .icon-20x20-bunker{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOpSURBVDhPrZVvTBNnHMe/d9dBaUtharBSwOsUZAwEGoECMUE0ZsSRZYvJtqjZJmOJY7ExkRczMe7VlizxxeaWGbNFXZbBZsziotmmAzVzVBQUwYppgRWo/Se29drrX67dc+eJMBzZCz/Jk+e55/l9v/f8ued31Ct79uFZQsv1kvR++7lOLPLjkjAlNfVyczGXj39pmBzq31NcVnY04A98aL9u0TqHBxxsdW1QDlnE3JL/OvGVIi4IJal0ajlD04WJ2XSTTr+q3bCmGGWVRoz+eRYunoHf60xzHP9NhoK6JKRS0zRFP8hkGFvjOx2zoo9CciPwiWRl88stA9aRW/C5vXh9xy40bNoMjVqD82fP4O3XGnHo2HnUNGykEiFfu9vtag/HKUnrvefaQKpBsT1n+JyCXldZ/iJatr8Fll0NU/V6rNBmwzpqRdieiy2NAjoPc2kSSuWtWAa2qEDSjd0Lkll71pHmQkMRlVqFv8fGEQ2HcO1qPzKSHCz9gzh2sBXaLAeWZ8xS4gqUKi0K89QQjRVCWFY/YsEpR/gIYjyH5MRFvGHogfnVBLo/qYC+vFga//HTUpz7rAI9R7egtRyIk9h/M2dIgeJnghzYNWsRy6vFoeNB/NJ1CZnaXDkCUCuzyAtzsb/zNL6/JmAZW4FZRiNp5ZAnhuR0o2IdCvPI0+nQ9GYHPv5dgbujIWlciPmhfF6DzoM/Yzy9EpWlq5Hm70tLfqwVmbdkyjk0OACPwybtoVhK8nOgz76DxP3r6BvkodSosO+DeuSoGEQiETimnLA73JL2kcc8Q4ahHNbhkW67zQ6PxwfH+Bg6tuWi73IQxq1HsN38E34758bGigIIZO+KCgvEE8eM1/erqJVMCHM3ha2qS07csPj0Rey74nMsGsVLqml8dNKG9fVb4X/gRe8Ih4k7VmRlqWFzBSlxdnzoYVvzbvO4ZEJYcPVeMJqm+s6cCmTn5LTosoGLdhpVpiYYTQ0YtlxATXU5kmo9YikaY64QfNOT5s3vmU/JcokFn43Ipra9X9y0XDkQiCvAFpdCqdZKt0WVmSmNuwIJ3J58SPbafoBmmCNS5zyemhzY6rorPV0nA7r8/BalUol4IgmPbQgeLokk5yUf/4S5affew2xVrax4wn9mG4Oxrv+PrhMuYtp6l9wOnvPD5+cwNel8v7nN/LUctogl05fBaLrR2/3dUCrqr4pFYsLMTGAn2ZIf5OGn8r8y9uPkSmbmkTqW4Bn/AoB/AEiQfn1r/m4bAAAAAElFTkSuQmCC")}#two-attack-view .icon-20x20-kill{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAARYSURBVDhPrVVtTFNXGH4uvZTSQgstpUKBUQQa0PFlBBzBZTB+bMMl02XBhbk6NhfjotFly2L2Yz/moiGZIZsxWSaRLRrJNtwSTIYwFBlBDCAwyrdQpIV+QmnpB+29ves9IJppsj8+ycn7vuec9+s5N++lqo+exPNExKZ8bniqwp7LF2g/w8RSFHbzdkXdiZtdly9QoRB7sHhnesq9UYOR4zBUUXd8jDj8B09VWL3/jSPdXUeXF7rr2tRKWTW/RwOCI5XKty4eXD83cCbpyrl3M1v4fT4RXwCvP4Ige/ceovz1Y0O8OtJz1mk2rqcwzqrJJj1kKaGF69dvZybGiT7UJkYVGQeg6B7yUCtcyNHyx63G0l059S+qIn74p7e3QN/X05ZeUBIkLXdeath2qvaVxRj3fWrIKDYVsqHkQx8UU8KkGJIstkgJ77IQ+uZLxJ7uleHqqndwX55U8r7OrzWYc3Dg077vtG/qjpOWw62dPvlxBkV0/5r6sIylBL/1gv2+HQ7PLFobruHa+Ub+eAvFS6tF3jF39sXPg5x7pB9gmTh+n7TsmBx6TXXfXJJrY7FfvkGJKCcTXM0uTNf3QzTPQWGj8KDPhwmLmEveE6SS8kSYmPFR8ys+amCUAa2MZe92dDVuPcq+sxWIT0vbtIBgpQbDZ9phsHmI/UgqbS5q+g7FWe0MsvIjkSCj8cX5t5EeIy5iEBJuBRQnZsCEAOx7d5DFB9spC796pmTzxmNYg17K50vF67oaSFOS8efVETh9PnJGAmo0as26ywn9QxvHZAmRqcvHS02VaJ3xYDwvG3s7vsKh4XrU3vkI+rgwJYpEHD5dBad5Gi7jIrQFa4h2uB4H3CG1++cGbqLms2gqThaCe3ENAUsPucCDMXVjebgN355qgn2VARxWdP78E2b6h3Him2KMDNpBq2kwTCiKBGwdXl9SZ72AmLQKSJML4fGJwi1oSTWDm62YpucRJxEQ3R2rgDMQCZ7H0SED8ooS4PaLyZnAph+kc3O3Vx14Nb40UpKKKGkc6AgWFOeCSuXH2qoTgRU7ggEGIpkI0hgG+VksHhp9EIWfIIpisLjgxYJPBsOi60tSIeW1lf9ywwuxPECyBFyTMJv8RNdu38jMQ+ZeJnJqfp3IWStLpNPDhqlwD4bJYCPKdMeYv0et7xmmbHAa5iCgrfCveaHWpEKplEMcGw1FcA2w2Inzk8hIFJC2bSsJCFLi5pd1xzjyYWuKSmx3O291WCYsZRKOTYiXR8A8OwPauoRInxeO5Y1qLWHeJOIIKMLc8ouTpqN/ToARo2NpeMr2TlpBcWhrOMi1hQstv9++MWf0luvHA0lCWgxangThtiREqZLhFSSAE6zjgUOJ8QUJJsw02u8Zlxz+yK8Hpiy15bpPCA/PnNj8sKAoqiI5QVqqUYnK5FECVYgJqa3ewBJ/bnKyvwrANc9bXWMimnbztBHHMP73F8DPPH4e8vqTjs8G8C/hq9/zbfBsvwAAAABJRU5ErkJggg==")}#two-attack-view .icon-20x20-killBig{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAARYSURBVDhPrVVtTFNXGH4uvZTSQgstpUKBUQQa0PFlBBzBZTB+bMMl02XBhbk6NhfjotFly2L2Yz/moiGZIZsxWSaRLRrJNtwSTIYwFBlBDCAwyrdQpIV+QmnpB+29ves9IJppsj8+ycn7vuec9+s5N++lqo+exPNExKZ8bniqwp7LF2g/w8RSFHbzdkXdiZtdly9QoRB7sHhnesq9UYOR4zBUUXd8jDj8B09VWL3/jSPdXUeXF7rr2tRKWTW/RwOCI5XKty4eXD83cCbpyrl3M1v4fT4RXwCvP4Ige/ceovz1Y0O8OtJz1mk2rqcwzqrJJj1kKaGF69dvZybGiT7UJkYVGQeg6B7yUCtcyNHyx63G0l059S+qIn74p7e3QN/X05ZeUBIkLXdeath2qvaVxRj3fWrIKDYVsqHkQx8UU8KkGJIstkgJ77IQ+uZLxJ7uleHqqndwX55U8r7OrzWYc3Dg077vtG/qjpOWw62dPvlxBkV0/5r6sIylBL/1gv2+HQ7PLFobruHa+Ub+eAvFS6tF3jF39sXPg5x7pB9gmTh+n7TsmBx6TXXfXJJrY7FfvkGJKCcTXM0uTNf3QzTPQWGj8KDPhwmLmEveE6SS8kSYmPFR8ys+amCUAa2MZe92dDVuPcq+sxWIT0vbtIBgpQbDZ9phsHmI/UgqbS5q+g7FWe0MsvIjkSCj8cX5t5EeIy5iEBJuBRQnZsCEAOx7d5DFB9spC796pmTzxmNYg17K50vF67oaSFOS8efVETh9PnJGAmo0as26ywn9QxvHZAmRqcvHS02VaJ3xYDwvG3s7vsKh4XrU3vkI+rgwJYpEHD5dBad5Gi7jIrQFa4h2uB4H3CG1++cGbqLms2gqThaCe3ENAUsPucCDMXVjebgN355qgn2VARxWdP78E2b6h3Him2KMDNpBq2kwTCiKBGwdXl9SZ72AmLQKSJML4fGJwi1oSTWDm62YpucRJxEQ3R2rgDMQCZ7H0SED8ooS4PaLyZnAph+kc3O3Vx14Nb40UpKKKGkc6AgWFOeCSuXH2qoTgRU7ggEGIpkI0hgG+VksHhp9EIWfIIpisLjgxYJPBsOi60tSIeW1lf9ywwuxPECyBFyTMJv8RNdu38jMQ+ZeJnJqfp3IWStLpNPDhqlwD4bJYCPKdMeYv0et7xmmbHAa5iCgrfCveaHWpEKplEMcGw1FcA2w2Inzk8hIFJC2bSsJCFLi5pd1xzjyYWuKSmx3O291WCYsZRKOTYiXR8A8OwPauoRInxeO5Y1qLWHeJOIIKMLc8ouTpqN/ToARo2NpeMr2TlpBcWhrOMi1hQstv9++MWf0luvHA0lCWgxangThtiREqZLhFSSAE6zjgUOJ8QUJJsw02u8Zlxz+yK8Hpiy15bpPCA/PnNj8sKAoqiI5QVqqUYnK5FECVYgJqa3ewBJ/bnKyvwrANc9bXWMimnbztBHHMP73F8DPPH4e8vqTjs8G8C/hq9/zbfBsvwAAAABJRU5ErkJggg==")}')
  }})),define('two/attackView/types/columns',[],(function(){return{ORIGIN_VILLAGE:'origin_village_name',COMMAND_TYPE:'command_type',TARGET_VILLAGE:'target_village_name',TIME_COMPLETED:'time_completed',ORIGIN_CHARACTER:'origin_character_name'}})),define('two/attackView/types/commands',[],(function(){return{ATTACK:'attack',SUPPORT:'support',RELOCATE:'relocate'}})),define('two/attackView/types/filters',[],(function(){return{COMMAND_TYPES:'command_types',VILLAGE:'village',INCOMING_UNITS:'incoming_units'}})),define('two/attackView/unitSpeedOrder',['conf/unitTypes'],(function(e){return[e.LIGHT_CAVALRY,e.HEAVY_CAVALRY,e.AXE,e.SWORD,e.RAM,e.SNOB,e.TREBUCHET]})),require(['two/ready','two/attackView','two/attackView/ui','two/attackView/events'],(function(e,t,a){if(t.isInitialized())return!1;e((function(){t.init(),a()}))})),define('two/autoCollector',['queues/EventQueue'],(function(e){let t=!1,o=!1,s=!0,c=0,d='',u=0,p=0,g=0,m=0,T=[],_=0;const A=function(){
  i.emit(l.RESOURCE_DEPOSIT_GET_INFO,{},(function(e){d=e.milestones[5].reward.animation,m=e.resources_collected,u=e.jobs.length,_=1e3*e.time_new_milestones-Date.now()+1e3,i.emit(l.GET_INVENTORY,{},(function(e){T=e.inventory,T.forEach((function(e){'resource_deposit_reroll'==e.type&&(g=e.amount,p=e.id)})),console.log(g,u,_,d,m),'food_capacity_increase'==d&&0==u&&(1==g&&m>=9e3&&_>=36e5?i.emit(l.PREMIUM_USE_ITEM,{village_id:n.getSelectedVillage().getId(),item_id:p}):2==g&&m>=8e3&&_>=73e5?i.emit(l.PREMIUM_USE_ITEM,{village_id:n.getSelectedVillage().getId(),item_id:p}):3==g&&m>=7100&&_>=111e5?i.emit(l.PREMIUM_USE_ITEM,{village_id:n.getSelectedVillage().getId(),item_id:p}):4==g&&m>=6200&&_>=15e6?i.emit(l.PREMIUM_USE_ITEM,{village_id:n.getSelectedVillage().getId(),item_id:p}):5==g&&m>=5400&&_>=19e6?i.emit(l.PREMIUM_USE_ITEM,{village_id:n.getSelectedVillage().getId(),item_id:p}):6==g&&m>=4600&&_>=231e5?i.emit(l.PREMIUM_USE_ITEM,{village_id:n.getSelectedVillage().getId(),item_id:p
  }):7==g&&m>=3900&&_>=273e5?i.emit(l.PREMIUM_USE_ITEM,{village_id:n.getSelectedVillage().getId(),item_id:p}):g>=8&&m>=3200&&_>=316e5&&i.emit(l.PREMIUM_USE_ITEM,{village_id:n.getSelectedVillage().getId(),item_id:p}))}))}))},f=function(){i.emit(l.RESOURCE_DEPOSIT_GET_INFO,{})},E=function(){if(!o)return!1;let e=n.getSelectedCharacter().getResourceDeposit();if(!e)return!1;if(e.getCurrentJob())return!1;let t=e.getCollectibleJobs();if(t)return a=t.shift(),void i.emit(l.RESOURCE_DEPOSIT_COLLECT,{job_id:a.id,village_id:n.getSelectedVillage().getId()});var a;let r=e.getReadyJobs();return r?function(e){i.emit(l.RESOURCE_DEPOSIT_START_JOB,{job_id:e.id})}(b(r)):void 0},b=function(e){return e.sort((function(e,t){return e.duration-t.duration}))[0]};let h={};return h.init=function(){t=!0,a.$on(r.RESOURCE_DEPOSIT_JOB_COLLECTIBLE,(function(){if(!s||!o)return!1;s=!1,setTimeout((function(){s=!0,E()}),1500),setTimeout((function(){A()}),2e4)})),a.$on(r.RESOURCE_DEPOSIT_JOBS_REROLLED,E),
  a.$on(r.RESOURCE_DEPOSIT_JOB_COLLECTED,E),a.$on(r.RESOURCE_DEPOSIT_INFO,(function(e,t){E(),function(e){const t=1e3*e.time_next_reset-Date.now()+1e3;clearTimeout(c),c=setTimeout(f,t)}(t)}))},h.start=function(){e.trigger(r.AUTO_COLLECTOR_STARTED),o=!0,E(),A()},h.stop=function(){e.trigger(r.AUTO_COLLECTOR_STOPPED),o=!1},h.isRunning=function(){return o},h.isInitialized=function(){return t},h})),define('two/autoCollector/events',[],(function(){angular.extend(r,{AUTO_COLLECTOR_STARTED:'auto_collector_started',AUTO_COLLECTOR_STOPPED:'auto_collector_stopped',AUTO_COLLECTOR_SECONDVILLAGE_STARTED:'auto_collector_secondvillage_started',AUTO_COLLECTOR_SECONDVILLAGE_STOPPED:'auto_collector_secondvillage_stopped'})})),define('two/autoCollector/ui',['two/ui','two/autoCollector','two/utils','queues/EventQueue'],(function(e,t,o,n){let i;return function(){return i=e.addMenuButton2('Kolekcjoner',50,g('i18n')('description',a.loc.ale,'auto_collector')),i.addEventListener('click',(function(){
  t.isRunning()?(t.stop(),t.secondVillage.stop(),o.notif('success',g('i18n')('deactivated',a.loc.ale,'auto_collector'))):(t.start(),t.secondVillage.start(),o.notif('success',g('i18n')('activated',a.loc.ale,'auto_collector')))})),n.register(r.AUTO_COLLECTOR_STARTED,(function(){i.classList.remove('btn-orange'),i.classList.add('btn-red')})),n.register(r.AUTO_COLLECTOR_STOPPED,(function(){i.classList.remove('btn-red'),i.classList.add('btn-orange')})),t.isRunning()&&n.trigger(r.AUTO_COLLECTOR_STARTED),opener}})),define('two/autoCollector/secondVillage',['two/autoCollector','two/utils','queues/EventQueue','helper/time','models/SecondVillageModel'],(function(e,t,o,s,c){let d=!1,u=!1,p=!1,g=injector.get('secondVillageService');const m=function(){var e;e=T,i.emit(l.SECOND_VILLAGE_GET_INFO,{},(function(t){if(g.hasFinishedLastJob(t.jobs))p=!0,i.emit(l.SECOND_VILLAGE_FINISH_VILLAGE),_.stop();else{let a=new c(t);n.getSelectedCharacter().setSecondVillage(a),e()}}))},T=function(){
  let e=n.getSelectedCharacter().getSecondVillage();if(!u||!e||!e.isAvailable())return!1;const a=function(e){const t=Date.now();for(let a in e)if(e[a].time_started&&e[a].time_completed&&t<s.server2ClientTime(e[a].time_completed))return e[a];return!1}(e.data.jobs);if(a){const e=s.server2ClientTime(a.time_completed)-Date.now()+1e3;return setTimeout(m,e),!1}const o=function(e){const t=Date.now();for(let a in e)if(e[a].time_started&&e[a].time_completed&&t>=s.server2ClientTime(e[a].time_completed)&&!e[a].collected)return a;return!1}(e.data.jobs);if(o)return r=o,void i.emit(l.SECOND_VILLAGE_COLLECT_JOB_REWARD,{village_id:n.getSelectedVillage().getId(),job_id:r});var r;const c=g.getCurrentDayJobs(e.data.jobs,e.data.day),d=g.getCollectedJobs(e.data.jobs),p=n.getSelectedVillage().getResources().getResources(),T=g.getAvailableJobs(c,d,p,[]);if(T){const e=function(e){let a=!1;return t.each(e,(function(e){return a=e,!1})),a}(T);!function(e,t){i.emit(l.SECOND_VILLAGE_START_JOB,{
  village_id:n.getSelectedVillage().getId(),job_id:e.id},t)}(e,(function(){const t=T[e];t?setTimeout(m,1e3*t.duration+1e3):setTimeout(m,6e4)}))}};let _={start:function(){if(!d||p)return!1;o.trigger(r.AUTO_COLLECTOR_SECONDVILLAGE_STARTED),u=!0,m()},stop:function(){if(!d)return!1;o.trigger(r.AUTO_COLLECTOR_SECONDVILLAGE_STOPPED),u=!1},isRunning:function(){return u},isInitialized:function(){return d},init:function(){if(!g.isFeatureActive())return!1;d=!0,i.emit(l.SECOND_VILLAGE_GET_INFO,{},(function(e){g.hasFinishedLastJob(e.jobs)?(p=!0,i.emit(l.SECOND_VILLAGE_FINISH_VILLAGE)):(a.$on(r.SECOND_VILLAGE_VILLAGE_CREATED,m),a.$on(r.SECOND_VILLAGE_JOB_COLLECTED,m))}))}};e.secondVillage=_})),require(['two/ready','two/autoCollector','two/autoCollector/ui','Lockr','queues/EventQueue','two/autoCollector/secondVillage','two/autoCollector/events'],(function(e,t,a,o,n){const i='auto_collector_active';if(t.isInitialized())return!1;e((function(){t.init(),t.secondVillage.init(),a(),e((function(){
  o.get(i,!1,!0)&&(t.start(),t.secondVillage.start()),n.register(r.AUTO_COLLECTOR_STARTED,(function(){o.set(i,!0)})),n.register(r.AUTO_COLLECTOR_STOPPED,(function(){o.set(i,!1)}))}),['initial_village'])}))})),define('two/autoFoundator',['two/utils','queues/EventQueue'],(function(e,t){let a=!1,o=!1;const s=function(){if(1==o){n.getSelectedCharacter().getVillageList().forEach((function(e,t){var a=e.getResources().getComputed(),o=a.wood,n=a.clay,r=a.iron,s=o.currentStock,c=n.currentStock,d=r.currentStock,u=Math.round(.02*s)+1,p=Math.round(.02*d)+1,g=Math.round(.02*c)+1;setTimeout((function(){i.emit(l.TRIBE_SKILL_DONATE,{village_id:e.getId(),crowns:0,resources:{wood:u,clay:g,iron:p}})}),t*Math.random()*3e3),console.log('Wykonano darowizne na plemię: '+e.getName()+' drewno: '+u+', glina: '+g+', żelazo: '+p)}))}};let c={init:function(){a=!0},start:function(){t.trigger(r.AUTO_FOUNDATOR_STARTED),o=!0,s(),setInterval((function(){s()}),6e3)},stop:function(){t.trigger(r.AUTO_FOUNDATOR_STOPPED),o=!1
  },isRunning:function(){return o},isInitialized:function(){return a}};return c})),define('two/autoFoundator/events',[],(function(){angular.extend(r,{AUTO_FOUNDATOR_STARTED:'auto_foundator_started',AUTO_FOUNDATOR_STOPPED:'auto_foundator_stopped'})})),define('two/autoFoundator/ui',['two/ui','two/autoFoundator','two/utils','queues/EventQueue'],(function(e,t,o,n){let i;return function(){return e.addDivisor3(71),i=e.addMenuButton3('Fundator',70,g('i18n')('description',a.loc.ale,'auto_foundator')),i.addEventListener('click',(function(){t.isRunning()?(t.stop(),o.notif('success',g('i18n')('deactivated',a.loc.ale,'auto_foundator'))):(t.start(),o.notif('success',g('i18n')('activated',a.loc.ale,'auto_foundator')))})),n.register(r.AUTO_FOUNDATOR_STARTED,(function(){i.classList.remove('btn-orange'),i.classList.add('btn-red')})),n.register(r.AUTO_FOUNDATOR_STOPPED,(function(){i.classList.remove('btn-red'),i.classList.add('btn-orange')})),t.isRunning()&&n.trigger(r.AUTO_FOUNDATOR_STARTED),opener}})),
  require(['two/ready','two/autoFoundator','two/autoFoundator/ui','Lockr','queues/EventQueue','two/autoFoundator/events'],(function(e,t,a,o,n){const i='auto_foundator_active';if(t.isInitialized())return!1;e((function(){t.init(),a(),e((function(){o.get(i,!1,!0)&&t.start(),n.register(r.AUTO_FOUNDATOR_STARTED,(function(){o.set(i,!0)})),n.register(r.AUTO_FOUNDATOR_STOPPED,(function(){o.set(i,!1)}))}),['initial_village'])}))})),define('two/autoHealer',['two/utils','queues/EventQueue'],(function(e,t){let a=!1,o=!1;const s=function(){if(1==o){console.log('Medyk uruchomiony'),n.getSelectedCharacter().getVillageList().forEach((function(e,t){let a=e.hospital.patients.healed;0==a.length?console.log('W wiosce '+e.getName()+' brak jednostek do wyleczenia'):setTimeout((function(){a.forEach((function(t,o){setTimeout((function(){i.emit(l.HOSPITAL_RELEASE_PATIENT,{village_id:e.getId(),patient_id:t.id})}),3e3*o),console.log('W wiosce: '+e.getName()+' wyleczono: '+a.units)}))}),5e3*t)}))}};let c={
  init:function(){a=!0},start:function(){t.trigger(r.AUTO_HEALER_STARTED),o=!0,s(),setInterval((function(){s()}),36e5)},stop:function(){t.trigger(r.AUTO_HEALER_STOPPED),o=!1},isRunning:function(){return o},isInitialized:function(){return a}};return c})),define('two/autoHealer/events',[],(function(){angular.extend(r,{AUTO_HEALER_STARTED:'auto_healer_started',AUTO_HEALER_STOPPED:'auto_healer_stopped'})})),define('two/autoHealer/ui',['two/ui','two/autoHealer','two/utils','queues/EventQueue'],(function(e,t,o,n){let i;return function(){return i=e.addMenuButton('Medyk',120,g('i18n')('description',a.loc.ale,'auto_healer')),i.addEventListener('click',(function(){t.isRunning()?(t.stop(),o.notif('success',g('i18n')('deactivated',a.loc.ale,'auto_healer'))):(t.start(),o.notif('success',g('i18n')('activated',a.loc.ale,'auto_healer')))})),n.register(r.AUTO_HEALER_STARTED,(function(){i.classList.remove('btn-orange'),i.classList.add('btn-red')})),n.register(r.AUTO_HEALER_STOPPED,(function(){
  i.classList.remove('btn-red'),i.classList.add('btn-orange')})),t.isRunning()&&n.trigger(r.AUTO_HEALER_STARTED),opener}})),require(['two/ready','two/autoHealer','two/autoHealer/ui','Lockr','queues/EventQueue','two/autoHealer/events'],(function(e,t,a,o,n){const i='auto_healer_active';if(t.isInitialized())return!1;e((function(){t.init(),a(),e((function(){o.get(i,!1,!0)&&t.start(),n.register(r.AUTO_HEALER_STARTED,(function(){o.set(i,!0)})),n.register(r.AUTO_HEALER_STOPPED,(function(){o.set(i,!1)}))}),['initial_village'])}))})),
  define('two/battleCalculator',['two/Settings','two/battleCalculator/settings','two/battleCalculator/settings/map','two/battleCalculator/settings/updates','two/battleCalculator/types/item','two/battleCalculator/types/level','two/battleCalculator/types/catapult-target','two/battleCalculator/types/order','two/battleCalculator/types/wall','two/battleCalculator/types/church','two/battleCalculator/types/weapon-master','two/battleCalculator/types/iron-walls','two/battleCalculator/types/clinique','two/battleCalculator/types/hospital','two/battleCalculator/types/training','two/ready','queues/EventQueue'],(function(e,t,o,s,c,d,u,p,g,m,T,_,A,f,E,b,h){
  let w,v,R=!1,S=!1,y=[],M=null,k=null,I=null,N=null,O=null,L=null,z=null,U=null,C=null,x=null,P=null,D=null,B=null,G=null,V=null,j=null,W=null,F=null,q=null,H=null,Y=null,K=null,Q=null,Z=null,X=null,J=null,$=null,ee=null,te=null,ae=null,oe=null,ne=null,ie=null,le=null,re=null,se=null,ce=null,de=null,ue=null,pe=null,ge=null,me=null,Te=null,_e=null,Ae=null,fe=null,Ee=null,be=null,he=null,we=null,ve=null,Re=null,Se=null,ye=null,Me=null,ke=null,Ie=null,Ne=null,Oe=null,Le=null,ze=null,Ue=null,Ce=null,xe=null,Pe=null,De=null,Be=null,Ge=null,Ve=null,je=null,We=null,Fe=null,qe=null,He=null,Ye=null,Ke=null,Qe=null,Ze=null,Xe=null,Je=null,$e=null,et=null,tt=null,at=null,ot=null,nt=null,it=null,lt=null,rt=null,st=null,ct=null,dt=null,ut=null,pt=null,gt=null,mt=null,Tt=null,_t=null,At=null,ft=null,Et=null,bt=null,ht=null,wt=null,vt=null,Rt=null,St=null,yt=null,Mt=null,kt=null,It=null,Nt=null,Ot=null,Lt=null,zt=null,Ut=null,Ct=null,xt=null,Pt=null,Dt=null,Bt=null,Gt=null,Vt=null,jt=null,Wt=null,Ft=null,qt=null,Ht=null,Yt=null,Kt=null,Qt=null,Zt=null,Xt=null,Jt=null,$t=null,ea=null,ta=null,aa=null,oa=null,na=null,ia=null,la=null,ra=null,sa=null,ca=null,da=null,ua=null,pa=null,ga=null,ma=null,Ta=null,_a=null,Aa=null,fa=null,Ea=null,ba=null,ha=null,wa=null,va=null,Ra=null,Sa=null,ya=null,Ma=null,ka=null,Ia=null,Na=null,Oa=null,La=null
  ;const za='battle_calculator_settings',Ua={[d.LEVEL_1]:'level_1',[d.LEVEL_2]:'level_2',[d.LEVEL_3]:'level_3'},Ca={[c.HALBERD_OF_GUAN_YU]:'battle.halberd',[c.PARACELSUS_LONGSWORD]:'battle.longsword',[c.THORGARDS_BATTLE_AXE]:'battle.battleax',[c.NIMRODS_LONGBOW]:'battle.longbow',[c.MIESZKOS_LANCE]:'battle.lance',[c.NIMRODS_COMPOSITE_BOW]:'battle.compositebow',[c.BAPTISTES_BANNER]:'battle.banner',[c.CAROLS_MORNING_STAR]:'battle.star',[c.ALETHEIAS_BONFIRE]:'battle.bonfire',[c.VASCOS_SCEPTER]:'battle.scepter'},xa={[u.HEADQUARTER]:'headquarter',[u.WAREHOUSE]:'warehouse',[u.FARM]:'farm',[u.RALLY_POINT]:'rally_point',[u.STATUE]:'statue',[u.WALL]:'wall',[u.TAVERN]:'tavern',[u.BARRACKS]:'barracks',[u.PRECEPTORY]:'preceptory',[u.HOSPITAL]:'hospital',[u.CLAY_PIT]:'clay_pit',[u.IRON_MINE]:'iron_mine',[u.TIMBER_CAMP]:'timber_camp',[u.CHAPEL]:'chapel',[u.CHURCH]:'church',[u.MARKET]:'market',[u.ACADEMY]:'academy'},Pa={[m.NO_CHURCH]:'without',[m.LEVEL_1]:'level_1',[m.LEVEL_2]:'level_2',
  [m.LEVEL_3]:'level_3'},Da={[g.NO_WALL]:'without',[g.LEVEL_1]:'level_1',[g.LEVEL_2]:'level_2',[g.LEVEL_3]:'level_3',[g.LEVEL_4]:'level_4',[g.LEVEL_5]:'level_5',[g.LEVEL_6]:'level_6',[g.LEVEL_7]:'level_7',[g.LEVEL_8]:'level_8',[g.LEVEL_9]:'level_9',[g.LEVEL_10]:'level_10',[g.LEVEL_11]:'level_11',[g.LEVEL_12]:'level_12',[g.LEVEL_13]:'level_13',[g.LEVEL_14]:'level_14',[g.LEVEL_15]:'level_15',[g.LEVEL_16]:'level_16',[g.LEVEL_17]:'level_17',[g.LEVEL_18]:'level_18',[g.LEVEL_19]:'level_19',[g.LEVEL_20]:'level_20'},Ba={[T.LEVEL_1]:'level_1',[T.LEVEL_2]:'level_2',[T.LEVEL_3]:'level_3',[T.LEVEL_4]:'level_4',[T.LEVEL_5]:'level_5'},Ga={[_.LEVEL_1]:'level_1',[_.LEVEL_2]:'level_2',[_.LEVEL_3]:'level_3',[_.LEVEL_4]:'level_4',[_.LEVEL_5]:'level_5'},Va={[A.LEVEL_1]:'level_1',[A.LEVEL_2]:'level_2',[A.LEVEL_3]:'level_3',[A.LEVEL_4]:'level_4',[A.LEVEL_5]:'level_5',[A.LEVEL_6]:'level_6',[A.LEVEL_7]:'level_7',[A.LEVEL_8]:'level_8',[A.LEVEL_9]:'level_9',[A.LEVEL_10]:'level_10'},ja={[f.LEVEL_1]:'level_1',
  [f.LEVEL_2]:'level_2',[f.LEVEL_3]:'level_3',[f.LEVEL_4]:'level_4',[f.LEVEL_5]:'level_5',[f.LEVEL_6]:'level_6',[f.LEVEL_7]:'level_7',[f.LEVEL_8]:'level_8',[f.LEVEL_9]:'level_9',[f.LEVEL_10]:'level_10'};console.log(ja,Va,Ga,Ba,Da,Pa,Ua,Ca,xa);const Wa=function(){y=[];const e=n.getPresetList().getPresets(),a=v[t.PRESET];y.push(e[a])},Fa={init:function(){R=!0,w=new e({settingsMap:o,storageKey:za}),w.onChange((function(e,t){v=w.getAll(),t[s.PRESETS]&&Wa()})),v=w.getAll(),console.log('battleCalculator settings',v),b((function(){Wa()}),'presets'),a.$on(r.ARMY_PRESET_UPDATE,Wa),a.$on(r.ARMY_PRESET_DELETED,Wa)},getPresetUnits:function(){M=v[t.PRESET],i.emit(l.GET_PRESETS,{},(function(e){for(La=0;La<e.presets.length;La++)e.presets[La].id==M&&(ia=e.presets[La].units.spear,la=e.presets[La].units.sword,ra=e.presets[La].units.axe,sa=e.presets[La].units.archer,ca=e.presets[La].units.light_cavalry,da=e.presets[La].units.mounted_archer,ua=e.presets[La].units.heavy_cavalry,pa=e.presets[La].units.ram,
  ga=e.presets[La].units.catapult,ma=e.presets[La].units.snob,Ta=e.presets[La].units.knight,_a=e.presets[La].units.trebuchet,Aa=e.presets[La].units.doppelsoldner)}))},getVillageUnits:function(){Oa=v[t.BATTLE_VILLAGE_ID],i.emit(l.VILLAGE_UNIT_INFO,{village_id:Oa},(function(e){Ea=e.available_units.axe.total,fa=e.available_units.archer.total,ba=e.available_units.catapult.total,ha=e.available_units.doppelsoldner.total,wa=e.available_units.heavy_cavalry.total,va=e.available_units.knight.total,Ra=e.available_units.light_cavalry.total,Sa=e.available_units.mounted_archer.total,ya=e.available_units.ram.total,Ma=e.available_units.snob.total,ka=e.available_units.spear.total,Ia=e.available_units.sword.total,Na=e.available_units.trebuchet.total}))},calculateB:function(){
  const e=v[t.BASHPOINTS_SPEAR],a=v[t.BASHPOINTS_SWORD],o=v[t.BASHPOINTS_AXE],n=v[t.BASHPOINTS_ARCHER],i=v[t.BASHPOINTS_LC],l=v[t.BASHPOINTS_MA],r=v[t.BASHPOINTS_HC],s=v[t.BASHPOINTS_RAM],c=v[t.BASHPOINTS_CATAPULT],d=v[t.BASHPOINTS_DOPPELSOLDNER],u=v[t.BASHPOINTS_TREBUCHET],p=v[t.BASHPOINTS_SNOB],g=v[t.BASHPOINTS_KNIGHT];var m=[1,2,4,2,13,12,15,8,10,20,200,0,25],T=[4,5,1,5,5,6,23,4,12,40,200,25,10];k=e*m[0]+a*m[1]+o*m[2]+n*m[3]+i*m[4]+l*m[5]+r*m[6]+s*m[7]+c*m[8]+g*m[9]+p*m[10]+u*m[11]+d*m[12],I=e*T[0]+a*T[1]+o*T[2]+n*T[3]+i*T[4]+l*T[5]+r*T[6]+s*T[7]+c*T[8]+g*T[9]+p*T[10]+u*T[11]+d*T[12]}};return Fa.simulate=function(){
  var e=[1,2,4,2,13,12,15,8,10,20,200,0,25],a=[4,5,1,5,5,6,23,4,12,40,200,25,10],o=[25,55,10,10,30,40,200,20,100,250,100,200,100],n=[45,5,5,30,40,30,160,50,50,400,50,250,100],i=[10,30,10,60,30,50,180,20,100,150,100,200,50],l=[10,25,45,25,130,150,150,2,100,150,30,30,300],r=[1,1,1,1,4,5,6,5,8,1,100,10,6],s=0,c=0,d=0,u=0,p=0,g=0,m=0,T=0,_=0,A=0,f=0,E=0,b=0,h=0,w=0,R=0,S=0,y=0,M=0,k=0,I=0,N=0,O=0,L=0,z=0,U=0,C=0,x=0,P=0,D=0,B=0,G=0,V=0,j=0,W=0,F=0,ia=0,la=0,ra=0,sa=0,ca=0,da=0,ua=0,pa=0,ga=0,ma=0,Ta=0,_a=0,Aa=0,fa=0,Ea=0,ba=0,ha=0,wa=0,va=0,Ra=0,Sa=0,ya=0,Ma=0,ka=0,Ia=0,Na=0,Oa=0,La=0,za=0,Ua=0,Ca=0,xa=0,Pa=0,Da=0,Ba=0,Ga=0,Va=0,ja=0,Wa=0,Fa=0,qa=0,Ha=0,Ya=0,Ka=0,Qa=0,Za=0,Xa=0,Ja=0,$a=0,eo=0,to=0,ao=0,oo=0,no=0,io=0,lo=0,ro=0,so=0,co=0,uo=0,po=0,go=0,mo=0,To=0,_o=0,Ao=0,fo=0,Eo=0,bo=0,ho=0,wo=0,vo=[1,1],Ro=[1,1],So=[1,1],yo=[1,1],Mo=[1,1],ko=[1,1],Io=[1,1],No=[1,1],Oo=[1,1],Lo=[1,1],zo=[1,1],Uo=[1,1],Co=[1,1],xo=[1,1],Po=[1,1],Do=[1,1],Bo=[1,1],Go=[1,1],Vo=[1,1],jo=[1,1],Wo=[1,1],Fo=[1,1],qo=[1,1],Ho=[1,1],Yo=[1,1],Ko=[1,1],Qo=[1,1],Zo=[1,1],Xo=[1,1],Jo=[1,1],$o=[1,1],en=[1,1],tn=[1,1],an=[1,1],on=[1,1],nn=[1,1],ln=0,rn=0,sn=0,cn=0,dn=0,un=0,pn=0,gn=0,mn=0,Tn=0,_n=0,An=0,fn=0,En=0,bn=0,hn=0,wn=0,vn=0,Rn=0,Sn=0,yn=0,Mn=0,kn=0,In=0,Nn=0,On=[1,1],Ln=0,zn=0,Un=0,Cn=0,xn=0,Pn=0,Dn=0,Bn=0,Gn=0,Vn=0,jn=0,Wn=0,Fn=0,qn=0,Hn=0,Yn=0,Kn=0,Qn=0,Zn=0,Xn=0,Jn=0,$n=0,ei=0,ti=0,ai=0,oi=0,ni=0,ii=0,li=0,ri=0,si=0,ci=0,di=0,ui=0,pi=0,gi=0,mi=0,Ti=[1,1],_i=[1,1],Ai=[1,1],fi=[1,1],Ei=[1,1],bi=[1,1],hi=[1,1],wi=[1,1],vi=[1,1],Ri=0,Si=0,yi=0,Mi=0,ki=0,Ii=0,Ni=0,Oi=0,Li=0,zi=0,Ui=0,Ci=0,xi=0,Pi=0,Di=0,Bi=0,Gi=0,Vi=0,ji=0,Wi=0,Fi=0,qi=0,Hi=0,Yi=0,Ki=0,Qi=0,Zi=0,Xi=0,Ji=0,$i=0,el=0,tl=0,al=0,ol=0,nl=0,il=0,ll=0,rl=0,sl=0,cl=0,dl=0,ul=0,pl=0,gl=0,ml=0,Tl=0,_l=0,Al=0,fl=0,El=0,bl=0,hl=0,wl=0,vl=0,Rl=0,Sl=0,yl=0,Ml=0,kl=0,Il=0,Nl=0,Ol=0,Ll=0,zl=0,Ul=0,Cl=0,xl=0,Pl=0,Dl=0,Bl=0,Gl=0,Vl=0,jl=0,Wl=0,Fl=0,ql=0,Hl=1,Yl=0,Kl=0,Ql=0,Zl=0,Xl=0,Jl=0,$l=0,er=0,tr=0,ar=0,or=0,nr=0,ir=0,lr=0,rr=0,sr=0,cr=0,dr=0,ur=0,pr=0,gr=0,mr=0,Tr=0,_r=0,Ar=0,fr=0,Er=0,br=0,hr=0,wr=0,vr=0,Rr=0,Sr=0,yr=0,Mr=0,kr=0,Ir=0,Nr=0,Or=0,Lr=0,zr=0,Ur=0,Cr=0,xr=0,Pr=0,Dr=0,Br=0,Gr=0,Vr=0,jr=0,Wr=0,Fr=0,qr=0,Hr=0,Yr=0,Kr=0,Qr=0,Zr=0,Xr=0,Jr=0,$r=0,es=0,ts=0,as=0,os=0,ns=0,is=0,ls=0,rs=0,ss=0,cs=0,ds=0,us=0,ps=0,gs=0,ms=0,Ts=0,_s=0,As=0,fs=0,Es=0,bs=0,hs=0,ws=0,vs=0,Rs=0,Ss=0,ys=0,Ms=0,ks=0,Is=0,Ns=0,Os=0,Ls=0,zs=0,Us=0,Cs=0,xs=0,Ps=0,Ds=0,Bs=0,Gs=0,Vs=0,js=0,Ws=0,Fs=0,qs=0,Hs=0,Ys=0,Ks=0,Qs=0,Zs=0,Xs=0,Js=0,$s=0,ec=0,tc=0,ac=0,oc=0,nc=0,ic=0,lc=0,rc=0,sc=0,cc=0,dc=0,uc=0,pc=0,gc=0,mc=0,Tc=0,_c=0,Ac=0,fc=0,Ec=0,bc=0,hc=0,wc=0,vc=0,Rc=0,Sc=0,yc=0,Mc=0,kc=0,Ic=0,Nc=0,Oc=0,Lc=0,zc=0,Uc=0,Cc=0,xc=0,Pc=0,Dc=0,Bc=0,Gc=0,Vc=0,jc=0,Wc=0,Fc=0,qc=0,Hc=0,Yc=0,Kc=0,Qc=0,Zc=0,Xc=0,Jc=0,$c=0,ed=0,td=0,ad=0,od=0,nd=0,id=0,ld=0,rd=0,sd=0,cd=0,dd=0,ud=0,pd=0,gd=0,md=0,Td=0,_d=0,Ad=0,fd=0,Ed=0,bd=0,hd=0,wd=0,vd=0,Rd=0
  ;console.log(oa,Kt),s=v[t.BATTLE_CHURCH_A],c=v[t.BATTLE_CHURCH_D],d=v[t.BATTLE_NIGHT_BONUS],u=v[t.BATTLE_OFFICER_LEADER],p=v[t.BATTLE_OFFICER_MEDIC],g=v[t.BATTLE_SKILL_MEDIC],m=v[t.BATTLE_SKILL_WEAPON_MASTER],T=v[t.BATTLE_WALL],_=v[t.BATTLE_SKILL_IRON_WALLS],A=v[t.BATTLE_SKILL_CLINIQUE],f=v[t.BATTLE_HOSPITAL],Ct=v[t.BATTLE_CATAPULT_TARGET],E=v[t.BATTLE_CATAPULT_TARGET],b=v[t.BATTLE_KNIGHT_ITEM_D1],h=v[t.BATTLE_KNIGHT_ITEM_D2],w=v[t.BATTLE_KNIGHT_ITEM_D3],R=v[t.BATTLE_KNIGHT_ITEM_A],S=v[t.BATTLE_ITEM_LEVEL_D1],y=v[t.BATTLE_ITEM_LEVEL_D2],M=v[t.BATTLE_ITEM_LEVEL_D3],k=v[t.BATTLE_ITEM_LEVEL_A],Ge=v[t.BATTLE_ARCHER_A],K=v[t.BATTLE_ARCHER_D],Be=v[t.BATTLE_AXE_A],Y=v[t.BATTLE_AXE_D],Qe=v[t.BATTLE_DOPPELSOLDNER_A],oe=v[t.BATTLE_DOPPELSOLDNER_D],Fe=v[t.BATTLE_CATAPULT_A],J=v[t.BATTLE_CATAPULT_D],Ke=v[t.BATTLE_HC_A],ae=v[t.BATTLE_HC_D],je=v[t.BATTLE_KNIGHT_A],Z=v[t.BATTLE_KNIGHT_D],He=v[t.BATTLE_LC_A],ee=v[t.BATTLE_LC_D],Ye=v[t.BATTLE_MA_A],te=v[t.BATTLE_MA_D],We=v[t.BATTLE_RAM_A],
  X=v[t.BATTLE_RAM_D],Ve=v[t.BATTLE_SNOB_A],Q=v[t.BATTLE_SNOB_D],Pe=v[t.BATTLE_SPEAR_A],q=v[t.BATTLE_SPEAR_D],De=v[t.BATTLE_SWORD_A],H=v[t.BATTLE_SWORD_D],qe=v[t.BATTLE_TREBUCHET_A],$=v[t.BATTLE_TREBUCHET_D],I=v[t.BATTLE_MORALE],N=v[t.BATTLE_LUCK],console.log(Ct),vd='level_1'==s?100:'level_2'==s?105:'level_3'==s?110:50,console.log(vd),Rd='level_1'==c?100:'level_2'==c?105:'level_3'==c?110:50,console.log(Rd),Hl=d?2:1,console.log(Hl),sl=u?.1:0,console.log(sl),Fl=p?.1:0,console.log(Fl),Jr=g?.1:0,console.log(Jr),Zr='level_1'==m?2:'level_2'==m?4:'level_3'==m?6:'level_4'==m?8:'level_5'==m?10:0,console.log(Zr),$r='level_1'==_?1:'level_2'==_?2:'level_3'==_?3:'level_4'==_?4:'level_5'==_?5:0,console.log($r),Xr='level_1'==A?100:'level_2'==A?200:'level_3'==A?300:'level_4'==A?400:'level_5'==A?500:'level_6'==A?600:'level_7'==A?700:'level_8'==A?800:'level_9'==A?900:'level_10'==A?1e3:0,console.log(Xr),
  Nn='level_1'==f?100:'level_2'==f?129:'level_3'==f?167:'level_4'==f?215:'level_5'==f?278:'level_6'==f?359:'level_7'==f?464:'level_8'==f?599:'level_9'==f?774:'level_10'==f?1e3:0,console.log(Nn),Gt='level_1'==T?1:'level_2'==T?2:'level_3'==T?3:'level_4'==T?4:'level_5'==T?5:'level_6'==T?6:'level_7'==T?7:'level_8'==T?8:'level_9'==T?9:'level_10'==T?10:'level_11'==T?11:'level_12'==T?12:'level_13'==T?13:'level_14'==T?14:'level_15'==T?15:'level_16'==T?16:'level_17'==T?17:'level_18'==T?18:'level_19'==T?19:'level_20'==T?20:0,console.log(Gt),Go='battle.lance'==b&'level_1'==S?[1.05,1.1]:'battle.lance'==b&'level_2'==S?[1.1,1.2]:'battle.lance'==b&'level_3'==S?[1.2,1.3]:[1,1],console.log(Go),ko='battle.battleaxe'==b&'level_1'==S?[1.05,1.1]:'battle.battleaxe'==b&'level_2'==S?[1.1,1.2]:'battle.battleaxe'==b&'level_3'==S?[1.2,1.3]:[1,1],console.log(ko),Fo='battle.compositebow'==b&'level_1'==S?[1.05,1.1]:'battle.compositebow'==b&'level_2'==S?[1.1,1.2]:'battle.compositebow'==b&'level_3'==S?[1.2,1.3]:[1,1],
  console.log(Fo),Jo='battle.halberd'==b&'level_1'==S?[1.1,1.05]:'battle.halberd'==b&'level_2'==S?[1.2,1.1]:'battle.halberd'==b&'level_3'==S?[1.3,1.2]:[1,1],console.log(Jo),an='battle.longsword'==b&'level_1'==S?[1.1,1.05]:'battle.longsword'==b&'level_2'==S?[1.2,1.1]:'battle.longsword'==b&'level_3'==S?[1.3,1.2]:[1,1],Ro='battle.longbow'==b&'level_1'==S?[1.1,1.05]:'battle.longbow'==b&'level_2'==S?[1.2,1.1]:'battle.longbow'==b&'level_3'==S?[1.3,1.2]:[1,1],xo='battle.banner'==b&'level_1'==S?[1.1,1.1]:'battle.banner'==b&'level_2'==S?[1.2,1.2]:'battle.banner'==b&'level_3'==S?[1.3,1.3]:[1,1],Ko='battle.star'==b&'level_1'==S?[1.05,1.25]:'battle.star'==b&'level_2'==S?[1.1,1.5]:'battle.star'==b&'level_3'==S?[1.2,2]:[1,1],Lo='battle.bonfire'==b&'level_1'==S?[1.05,1.25]:'battle.bonfire'==b&'level_2'==S?[1.1,1.5]:'battle.bonfire'==b&'level_3'==S?[1.2,2]:[1,1],Vo='battle.lance'==h&'level_1'==y?[1.05,1.1]:'battle.lance'==h&'level_2'==y?[1.1,1.2]:'battle.lance'==h&'level_3'==y?[1.2,1.3]:[1,1],
  Io='battle.battleaxe'==h&'level_1'==y?[1.05,1.1]:'battle.battleaxe'==h&'level_2'==y?[1.1,1.2]:'battle.battleaxe'==h&'level_3'==y?[1.2,1.3]:[1,1],qo='battle.compositebow'==h&'level_1'==y?[1.05,1.1]:'battle.compositebow'==h&'level_2'==y?[1.1,1.2]:'battle.compositebow'==h&'level_3'==y?[1.2,1.3]:[1,1],$o='battle.halberd'==h&'level_1'==y?[1.1,1.05]:'battle.halberd'==h&'level_2'==y?[1.2,1.1]:'battle.halberd'==h&'level_3'==y?[1.3,1.2]:[1,1],on='battle.longsword'==h&'level_1'==y?[1.1,1.05]:'battle.longsword'==h&'level_2'==y?[1.2,1.1]:'battle.longsword'==h&'level_3'==y?[1.3,1.2]:[1,1],So='battle.longbow'==h&'level_1'==y?[1.1,1.05]:'battle.longbow'==h&'level_2'==y?[1.2,1.1]:'battle.longbow'==h&'level_3'==y?[1.3,1.2]:[1,1],Po='battle.banner'==h&'level_1'==y?[1.1,1.1]:'battle.banner'==h&'level_2'==y?[1.2,1.2]:'battle.banner'==h&'level_3'==y?[1.3,1.3]:[1,1],Qo='battle.star'==h&'level_1'==y?[1.05,1.25]:'battle.star'==h&'level_2'==y?[1.1,1.5]:'battle.star'==h&'level_3'==y?[1.2,2]:[1,1],
  zo='battle.bonfire'==h&'level_1'==y?[1.05,1.25]:'battle.bonfire'==h&'level_2'==y?[1.1,1.5]:'battle.bonfire'==h&'level_3'==y?[1.2,2]:[1,1],jo='battle.lance'==w&'level_1'==M?[1.05,1.1]:'battle.lance'==w&'level_2'==M?[1.1,1.2]:'battle.lance'==w&'level_3'==M?[1.2,1.3]:[1,1],No='battle.battleaxe'==w&'level_1'==M?[1.05,1.1]:'battle.battleaxe'==w&'level_2'==M?[1.1,1.2]:'battle.battleaxe'==w&'level_3'==M?[1.2,1.3]:[1,1],Ho='battle.compositebow'==w&'level_1'==M?[1.05,1.1]:'battle.compositebow'==w&'level_2'==M?[1.1,1.2]:'battle.compositebow'==w&'level_3'==M?[1.2,1.3]:[1,1],en='battle.halberd'==w&'level_1'==M?[1.1,1.05]:'battle.halberd'==w&'level_2'==M?[1.2,1.1]:'battle.halberd'==w&'level_3'==M?[1.3,1.2]:[1,1],nn='battle.longsword'==w&'level_1'==M?[1.1,1.05]:'battle.longsword'==w&'level_2'==M?[1.2,1.1]:'battle.longsword'==w&'level_3'==M?[1.3,1.2]:[1,1],yo='battle.longbow'==w&'level_1'==M?[1.1,1.05]:'battle.longbow'==w&'level_2'==M?[1.2,1.1]:'battle.longbow'==w&'level_3'==M?[1.3,1.2]:[1,1],
  Do='battle.banner'==w&'level_1'==M?[1.1,1.1]:'battle.banner'==w&'level_2'==M?[1.2,1.2]:'battle.banner'==w&'level_3'==M?[1.3,1.3]:[1,1],Zo='battle.star'==w&'level_1'==M?[1.05,1.25]:'battle.star'==w&'level_2'==M?[1.1,1.5]:'battle.star'==w&'level_3'==M?[1.2,2]:[1,1],Uo='battle.bonfire'==w&'level_1'==M?[1.05,1.25]:'battle.bonfire'==w&'level_2'==M?[1.1,1.5]:'battle.bonfire'==w&'level_3'==M?[1.2,2]:[1,1],Bo=Go[0]>=Vo[0]&&Go[0]>=jo[0]?Go:Vo[0]>=Go[0]&&Vo[0]>=jo[0]?Vo:jo[0]>=Go[0]&&jo[0]>=Vo[0]?jo:[1,1],Mo=ko[0]>=Io[0]&&ko[0]>=No[0]?ko:Io[0]>=ko[0]&&Io[0]>=No[0]?Io:No[0]>=ko[0]&&No[0]>=Io[0]?No:[1,1],Wo=Fo[0]>=qo[0]&&Fo[0]>=Ho[0]?Fo:qo[0]>=Fo[0]&&qo[0]>=Ho[0]?qo:Ho[0]>=Fo[0]&&Ho[0]>=qo[0]?Ho:[1,1],Co=xo[0]>=Po[0]&&xo[0]>=Do[0]?xo:Po[0]>=xo[0]&&Po[0]>=Do[0]?Po:Do[0]>=xo[0]&&Do[0]>=Po[0]?Do:[1,1],Yo=Ko[0]>=Qo[0]&&Ko[0]>=Zo[0]?Ko:Qo[0]>=Ko[0]&&Qo[0]>=Zo[0]?Qo:Zo[0]>=Ko[0]&&Zo[0]>=Qo[0]?Zo:[1,1],Oo=Lo[0]>=zo[0]&&Lo[0]>=Uo[0]?Lo:zo[0]>=Lo[0]&&zo[0]>=Uo[0]?zo:Uo[0]>=Lo[0]&&Uo[0]>=zo[0]?Uo:[1,1],
  Xo=Jo[0]>=$o[0]&&Jo[0]>=en[0]?Jo:$o[0]>=Jo[0]&&$o[0]>=en[0]?$o:en[0]>=Jo[0]&&en[0]>=$o[0]?en:[1,1],tn=an[0]>=on[0]&&an[0]>=nn[0]?an:on[0]>=an[0]&&on[0]>=nn[0]?on:nn[0]>=an[0]&&nn[0]>=on[0]?nn:[1,1],vo=Ro[0]>=So[0]&&Ro[0]>=yo[0]?Ro:So[0]>=Ro[0]&&So[0]>=yo[0]?So:yo[0]>=Ro[0]&&yo[0]>=So[0]?yo:[1,1],Ei='battle.lance'==R&'level_1'==k?[1.05,1.1]:'battle.lance'==R&'level_2'==k?[1.1,1.2]:'battle.lance'==R&'level_3'==k?[1.2,1.3]:[1,1],_i='battle.battleaxe'==R&'level_1'==k?[1.05,1.1]:'battle.battleaxe'==R&'level_2'==k?[1.1,1.2]:'battle.battleaxe'==R&'level_3'==k?[1.2,1.3]:[1,1],bi='battle.compositebow'==R&'level_1'==k?[1.05,1.1]:'battle.compositebow'==R&'level_2'==k?[1.1,1.2]:'battle.compositebow'==R&'level_3'==k?[1.2,1.3]:[1,1],wi='battle.halberd'==R&'level_1'==k?[1.1,1.05]:'battle.halberd'==R&'level_2'==k?[1.2,1.1]:'battle.halberd'==R&'level_3'==k?[1.3,1.2]:[1,1],
  vi='battle.longsword'==R&'level_1'==k?[1.1,1.05]:'battle.longsword'==R&'level_2'==k?[1.2,1.1]:'battle.longsword'==R&'level_3'==k?[1.3,1.2]:[1,1],Ti='battle.longbow'==R&'level_1'==k?[1.1,1.05]:'battle.longbow'==R&'level_2'==k?[1.2,1.1]:'battle.longbow'==R&'level_3'==k?[1.3,1.2]:[1,1],fi='battle.banner'==R&'level_1'==k?[1.1,1.1]:'battle.banner'==R&'level_2'==k?[1.2,1.2]:'battle.banner'==R&'level_3'==k?[1.3,1.3]:[1,1],hi='battle.star'==R&'level_1'==k?[1.05,1.25]:'battle.star'==R&'level_2'==k?[1.1,1.5]:'battle.star'==R&'level_3'==k?[1.2,2]:[1,1],Ai='battle.bonfire'==R&'level_1'==k?[1.05,1.25]:'battle.bonfire'==R&'level_2'==k?[1.1,1.5]:'battle.bonfire'==R&'level_3'==k?[1.2,2]:[1,1],Xt=Pe*r[0]+De*r[1]+Be*r[2]+Ge*r[3]+He*r[4]+Ye*r[5]+Ke*r[6]+We*r[7]+Fe*r[8]+je*r[9]+Ve*r[10]+qe*r[11]+Qe*r[12],jt=q*r[0]+H*r[1]+Y*r[2]+K*r[3]+ee*r[4]+te*r[5]+ae*r[6]+X*r[7]+J*r[8]+Z*r[9]+Q*r[10]+$*r[11]+oe*r[12],console.log('Prowiant:'+Xt,jt),na=2*Xt<=jt?2:1,Yl=Xt-5*We,
  Kl=0==Gt?0:Math.round(20*Math.pow(1.2515,Gt-1)),wo=jt+(Kl+100*Q),Er=0==Yl||0==wo?0:Yl/wo>1?1:Yl/wo,console.log(Er),ql=I/100,Rl=N/100,Zt=Math.round(1e3*(vd/100*ql*(1+Rl)+Zr/100+sl))/1e3,bd=Fe/(Ed=We+Fe),hd=We/Ed,ed=Math.round($*bd),Td=Math.round($*hd),Vr=(Wr=We-(0==We?0:We<Td?We:Td))*Er*Zt*(On='battle.star'==R?'level_1'==k?[1.05,1.25]:'level_2'==k?[1.1,1.5]:'level_3'==k?[1.2,2]:[1,1]:[1,1])[1],kn=20==Gt?18:19==Gt?17:18==Gt?15:17==Gt?14:16==Gt?13:15==Gt?11:14==Gt?10:13==Gt||12==Gt?9:11==Gt?8:10==Gt?7:9==Gt||8==Gt?6:7==Gt||6==Gt?5:5==Gt||4==Gt||3==Gt?4:2==Gt||1==Gt?3:0,Ad=0==Gt?0:2*kn,_d=0==Gt?0:Vr/Ad,fd=Gt<=$r?Gt:Gt-$r<_d?Gt<$r?Gt:$r:Gt-_d,La=Math.round(fd),Oa=Math.round(.05*La*1e3)/1e3,Vt=Math.round(Rd/100*(1+Oa)*Hl*1e3)/1e3,ho=Fe-(0==Fe?0:Fe<ed?Fe:ed),Dt=Math.round(fd),Na=0==Dt?0:Math.round(20*Math.pow(1.2515,Dt-1)),ji=Pe+De+Be+Qe>He+Ke&&Pe+De+Be+Qe>Ge+Ye?'inf':He+Ke>Pe+De+Be+Qe&&He+Ke>Ge+Ye?'cav':'arc',
  nr=Pe*r[0]+De*r[1]+Be*r[2]+Ge*r[3]+He*r[4]+Ye*r[5]+Ke*r[6]+Wr*r[7]+ho*r[8]+je*r[9]+Ve*r[10]+qe*r[11]+Qe*r[12],_r=q*r[0]+H*r[1]+Y*r[2]+K*r[3]+ee*r[4]+te*r[5]+ae*r[6]+X*r[7]+J*r[8]+Z*r[9]+Q*r[10]+$*r[11]+oe*r[12],console.log(nr,_r),qr=Wr*r[7]+ho*r[8]+qe*r[11]+je*r[9]+Ve*r[10],'cav'==ji?(Jl=He*r[4]+Ke*r[6]+qr,tr=Pe*r[0]+De*r[1]+Be*r[2]+Qe*r[12],Ql=Ge*r[3]+Ye*r[5]):'inf'==ji?(tr=Pe*r[0]+De*r[1]+Be*r[2]+Qe*r[12]+qr,Jl=He*r[4]+Ke*r[6],Ql=Ge*r[3]+Ye*r[5]):'arc'==ji?(tr=Pe*r[0]+De*r[1]+Be*r[2]+Qe*r[12],Jl=He*r[4]+Ke*r[6],Ql=Ge*r[3]+Ye*r[5]+qr):(tr=Pe*r[0]+De*r[1]+Be*r[2]+Qe*r[12],Jl=He*r[4]+Ke*r[6],Ql=Ge*r[3]+Ye*r[5]),Al=0==tr?0:Math.round(tr/nr*1e4)/1e4,_l=0==Jl?0:Math.round(Jl/nr*1e4)/1e4,Tl=0==Ql?0:Math.round(Ql/nr*1e4)/1e4,console.log(Tl),Ns=Math.round(q*Al),Is=Math.round(q*_l),ks=q-Is-Ns,Vc=Math.round(H*Al),Gc=Math.round(H*_l),Bc=H-Gc-Vc,wa=Math.round(Y*Al),ha=Math.round(Y*_l),ba=Y-ha-wa,j=Math.round(K*Al),V=Math.round(K*_l),G=K-V-j,el=Math.round(ee*Al),$i=Math.round(ee*_l),Ji=ee-$i-el,
  xl=Math.round(te*Al),Cl=Math.round(te*_l),Ul=te-Cl-xl,En=Math.round(ae*Al),fn=Math.round(ae*_l),An=ae-fn-En,Or=Math.round(X*Al),Nr=Math.round(X*_l),Ir=X-Nr-Or,co=Math.round(J*Al),so=Math.round(J*_l),ro=J-so-co,us=Math.round(Q*Al),ds=Math.round(Q*_l),cs=Q-ds-us,rd=Math.round($*Al),ld=Math.round($*_l),id=$-ld-rd,Fa=Math.round(oe*Al),Wa=Math.round(oe*_l),ja=oe-Wa-Fa,ri=Math.round(Z*Al),li=Math.round(Z*_l),ii=Z-li-ri,gr=Ns*r[0]+Vc*r[1]+wa*r[2]+Fa*r[12]+us*r[10]+el*r[4]+En*r[6]+j*r[3]+xl*r[5]+Or*r[7]+co*r[8]+rd*r[11]+ri*r[9],dr=Is*r[0]+Gc*r[1]+ha*r[2]+Wa*r[12]+ds*r[10]+$i*r[4]+fn*r[6]+V*r[3]+Cl*r[5]+Nr*r[7]+so*r[8]+ld*r[11]+li*r[9],rr=ks*r[0]+Bc*r[1]+ba*r[2]+ja*r[12]+cs*r[10]+Ji*r[4]+An*r[6]+G*r[3]+Ul*r[5]+Ir*r[7]+ro*r[8]+id*r[11]+ii*r[9],console.log(rr,dr,gr),dc=Ns*o[0]*Xo[0]+Vc*o[1]*tn[0]+wa*o[2]*Mo[0]+Fa*o[12]+us*o[10]+el*o[4]*Bo[0]+En*o[6]*Co[0]+j*o[3]*vo[0]+xl*o[5]*Wo[0]+Or*o[7]*Yo[0]+co*o[8]*Oo[0]+rd*o[11]+ri*o[9],
  rc=Is*n[0]*Xo[0]+Gc*n[1]*tn[0]+ha*n[2]*Mo[0]+Wa*n[12]+ds*n[10]+$i*n[4]*Bo[0]+fn*n[6]*Co[0]+V*n[3]*vo[0]+Cl*n[5]*Wo[0]+Nr*n[7]*Yo[0]+so*n[8]*Oo[0]+ld*n[11]+li*n[9],nc=ks*i[0]*Xo[0]+Bc*i[1]*tn[0]+ba*i[2]*Mo[0]+ja*i[12]+cs*i[10]+Ji*i[4]*Bo[0]+An*i[6]*Co[0]+G*i[3]*vo[0]+Ul*i[5]*Wo[0]+Ir*i[7]*Yo[0]+ro*i[8]*Oo[0]+id*i[11]+ii*i[9],Kr=Wr*l[7]*hi[1]+ho*l[8]*Ai[1]+qe*l[11]+je*l[9]+Ve*l[10],'cav'==ji?(Js=He*l[4]*Ei[1]+Ke*l[6]*fi[1]+Kr,tc=Pe*l[0]*wi[1]+De*l[1]*vi[1]+Be*l[2]*_i[1]+Qe*l[12]*na,Qs=Ge*l[3]*Ti[1]+Ye*l[5]*bi[1]):'inf'==ji?(tc=Pe*l[0]*wi[1]+De*l[1]*vi[1]+Be*l[2]*_i[1]+Qe*l[12]*na+Kr,Js=He*l[4]*Ei[1]+Ke*l[6]*fi[1],Qs=Ge*l[3]*Ti[1]+Ye*l[5]*bi[1]):'arc'==ji?(tc=Pe*l[0]*wi[1]+De*l[1]*vi[1]+Be*l[2]*_i[1]+Qe*l[12]*na,Js=He*l[4]*Ei[1]+Ke*l[6]*fi[1],Qs=Ge*l[3]*Ti[1]+Ye*l[5]*bi[1]+Kr):(tc=Pe*l[0]*wi[1]+De*l[1]*vi[1]+Be*l[2]*_i[1]+Qe*l[12]*na,Js=He*l[4]*Ei[1]+Ke*l[6]*fi[1],Qs=Ge*l[3]*Ti[1]+Ye*l[5]*bi[1]),Jt=(Fs=tc*Zt)+(Vs=Js*Zt)+(Ds=Qs*Zt),Wt=(Ec=dc*Vt+Na)+(_c=rc*Vt+Na)+(gc=nc*Vt+Na),
  console.log(Jt,Wt),Gi=0==Fs||0==Ec?0:Ec<=Fs?1:Math.pow(Fs/Ec,.5)/(Ec/Fs),Pi=0==Vs||0==_c?0:_c<=Vs?1:Math.pow(Vs/_c,.5)/(_c/Vs),Ui=0==Ds||0==gc?0:gc<=Ds?1:Math.pow(Ds/gc,.5)/(gc/Ds),Oi=0==Fs||0==Ec?0:Fs<=Ec?1:Math.pow(Ec/Fs,.5)/(Fs/Ec),ki=0==Vs||0==_c?0:Vs<=_c?1:Math.pow(_c/Vs,.5)/(Vs/_c),Si=0==Ds||0==gc?0:Ds<=gc?1:Math.pow(gc/Ds,.5)/(Ds/gc),Un=Math.round(1e6*Oi)/1e6,zn=Math.round(1e6*ki)/1e6,Ln=Math.round(1e6*Si)/1e6,Pn=Math.round(1e6*Gi)/1e6,xn=Math.round(1e6*Pi)/1e6,Cn=Math.round(1e6*Ui)/1e6,Rs=Math.round(-Ns*Pn+1e-6)+Math.round(-Is*xn+1e-6)+Math.round(-ks*Cn+1e-6),Cc=Math.round(-Vc*Pn+1e-6)+Math.round(-Gc*xn+1e-6)+Math.round(-Bc*Cn+1e-6),_a=Math.round(-wa*Pn+1e-6)+Math.round(-ha*xn+1e-6)+Math.round(-ba*Cn+1e-6),x=Math.round(-j*Pn+1e-6)+Math.round(-V*xn+1e-6)+Math.round(-G*Cn+1e-6),Ki=Math.round(-el*Pn+1e-6)+Math.round(-$i*xn+1e-6)+Math.round(-Ji*Cn+1e-6),Nl=Math.round(-xl*Pn+1e-6)+Math.round(-Cl*xn+1e-6)+Math.round(-Ul*Cn+1e-6),
  gn=Math.round(-En*Pn+1e-6)+Math.round(-fn*xn+1e-6)+Math.round(-An*Cn+1e-6),Sr=Math.round(-Or*Pn+1e-6)+Math.round(-Nr*xn+1e-6)+Math.round(-Ir*Cn+1e-6),oo=Math.round(-co*Pn+1e-6)+Math.round(-so*xn+1e-6)+Math.round(-ro*Cn+1e-6),td=Math.round(-rd*Pn+1e-6)+Math.round(-ld*xn+1e-6)+Math.round(-id*Cn+1e-6),Da=Math.round(-Fa*Pn+1e-6)+Math.round(-Wa*xn+1e-6)+Math.round(-ja*Cn+1e-6),ti=Math.round(-ri*Pn+1e-6)+Math.round(-li*xn+1e-6)+Math.round(-ii*Cn+1e-6),is=Math.round(-us*Pn+1e-6)+Math.round(-ds*xn+1e-6)+Math.round(-cs*Cn+1e-6),Es=Math.round(-Pe*Un+1e-6),Nc=Math.round(-De*Un+1e-6),ua=Math.round(-Be*Un+1e-6),za=Math.round(-Qe*Un+1e-6),O=Math.round(-Ge*Ln+1e-6),Sl=Math.round(-Ye*Ln+1e-6),Wi=Math.round(-He*zn+1e-6),sn=Math.round(-Ke*zn+1e-6),'cav'==ji?(br=Math.round(-Wr*zn+1e-6),Ja=Math.round(-ho*zn+1e-6),Qc=Math.round(-qe*zn+1e-6),Zn=Math.round(-je*zn+1e-6),es=Math.round(-Ve*zn+1e-6)):'inf'==ji?(br=Math.round(-Wr*Un+1e-6),Ja=Math.round(-ho*Un+1e-6),Qc=Math.round(-qe*Un+1e-6),
  Zn=Math.round(-je*Un+1e-6),es=Math.round(-Ve*Un+1e-6)):'arc'==ji&&(br=Math.round(-Wr*Ln+1e-6),Ja=Math.round(-ho*Ln+1e-6),Qc=Math.round(-qe*Ln+1e-6),Zn=Math.round(-je*Ln+1e-6),es=Math.round(-Ve*Ln+1e-6)),ws=Pe+Es,zc=De+Nc,ma=Be+ua,U=Ge+O,Hi=He+Wi,kl=Ye+Sl,un=Ke+sn,vr=Wr+br,to=ho+Ja,Jc=qe+Qc,xa=Qe+za,os=Ve+es,$n=je+Zn,Ms=q+Rs,Dc=H+Cc,Ea=Y+_a,B=K+x,Xi=ee+Ki,zl=te+Nl,_n=ae+gn,kr=X+Sr,lo=J+oo,nd=$+td,Va=oe+Da,ss=Q+is,ni=Z+ti,cl=Es*r[0]+Nc*r[1]+ua*r[2]+O*r[3]+Wi*r[4]+Sl*r[5]+sn*r[6]+br*r[7]+Ja*r[8]+Zn*r[9]+es*r[10]+Qc*r[11]+za*r[12],pl=Rs*r[0]+Cc*r[1]+_a*r[2]+x*r[3]+Ki*r[4]+Nl*r[5]+gn*r[6]+Sr*r[7]+oo*r[8]+ti*r[9]+is*r[10]+td*r[11]+Da*r[12],console.log(cl,pl),Rc=ws*r[0]+zc*r[1]+ma*r[2]+U*r[3]+Hi*r[4]+kl*r[5]+un*r[6]+vr*r[7]+to*r[8]+Jc*r[11]+xa*r[12]+os*r[10]+$n*r[9],Mc=Ms*r[0]+Dc*r[1]+Ea*r[2]+B*r[3]+Xi*r[4]+zl*r[5]+_n*r[6]+kr*r[7]+lo*r[8]+nd*r[11]+Va*r[12]+ss*r[10]+ni*r[9],console.log(Mc,Rc),da=0==Jt||0==Wt?0:Wt<=Jt?1:Math.pow(Jt/Wt,.5/(Wt/Jt)),console.log(da),
  ir=ws*r[0]+zc*r[1]+ma*r[2]+U*r[3]+Hi*r[4]+kl*r[5]+un*r[6]+vr*r[7]+to*r[8]+$n*r[9]+os*r[10]+Jc*r[11]+xa*r[12],Ar=Ms*r[0]+Dc*r[1]+Ea*r[2]+B*r[3]+Xi*r[4]+zl*r[5]+_n*r[6]+kr*r[7]+lo*r[8]+ni*r[9]+ss*r[10]+nd*r[11]+Va*r[12],console.log(ir,Ar),Hr=vr*r[7]+to*r[8]+Jc*r[11]+$n*r[9],'cav'==ji?($l=Hi*r[4]+un*r[6]+Hr,ar=ws*r[0]+zc*r[1]+ma*r[2]+xa*r[12]+os*r[10],Zl=U*r[3]+kl*r[5]):'inf'==ji?(ar=ws*r[0]+zc*r[1]+ma*r[2]+xa*r[12]+os*r[10]+Hr,$l=Hi*r[4]+un*r[6],Zl=U*r[3]+kl*r[5]):'arc'==ji?(ar=ws*r[0]+zc*r[1]+ma*r[2]+xa*r[12]+os*r[10],$l=Hi*r[4]+un*r[6],Zl=U*r[3]+kl*r[5]+Hr):(ar=ws*r[0]+zc*r[1]+ma*r[2]+xa*r[12]+os*r[10],$l=Hi*r[4]+un*r[6],Zl=U*r[3]+kl*r[5]),bl=0==ar?0:Math.round(ar/ir*1e4)/1e4,El=0==$l?0:Math.round($l/ir*1e4)/1e4,fl=0==Zl?0:Math.round(Zl/ir*1e4)/1e4,console.log(fl),Us=Math.round(Ms*bl),zs=Math.round(Ms*El),Ls=Ms-zs-Us,qc=Math.round(Dc*bl),Fc=Math.round(Dc*El),Wc=Dc-Fc-qc,ya=Math.round(Ea*bl),Sa=Math.round(Ea*El),Ra=Ea-Sa-ya,la=Math.round(B*bl),ia=Math.round(B*El),F=B-ia-la,
  nl=Math.round(Xi*bl),ol=Math.round(Xi*El),al=Xi-ol-nl,Gl=Math.round(zl*bl),Bl=Math.round(zl*El),Dl=zl-Bl-Gl,vn=Math.round(_n*bl),wn=Math.round(_n*El),hn=_n-wn-vn,Cr=Math.round(kr*bl),Ur=Math.round(kr*El),zr=kr-Ur-Cr,mo=Math.round(lo*bl),go=Math.round(lo*El),po=lo-go-mo,Ts=Math.round(ss*bl),ms=Math.round(ss*El),gs=ss-ms-Ts,ud=Math.round(nd*bl),dd=Math.round(nd*El),cd=nd-dd-ud,Ka=Math.round(Va*bl),Ya=Math.round(Va*El),Ha=Va-Ya-Ka,ui=Math.round(ni*bl),di=Math.round(ni*El),ci=ni-di-ui,mr=Us*r[0]+qc*r[1]+ya*r[2]+Ka*r[12]+Ts*r[10]+nl*r[4]+vn*r[6]+la*r[3]+Gl*r[5]+Cr*r[7]+mo*r[8]+ud*r[11]+ui*r[9],ur=zs*r[0]+Fc*r[1]+Sa*r[2]+Ya*r[12]+ms*r[10]+ol*r[4]+wn*r[6]+ia*r[3]+Bl*r[5]+Ur*r[7]+go*r[8]+dd*r[11]+di*r[9],sr=Ls*r[0]+Wc*r[1]+Ra*r[2]+Ha*r[12]+gs*r[10]+al*r[4]+hn*r[6]+F*r[3]+Dl*r[5]+zr*r[7]+po*r[8]+cd*r[11]+ci*r[9],console.log(sr,ur,mr),
  uc=Us*o[0]*Xo[0]+qc*o[1]*tn[0]+ya*o[2]*Mo[0]+Ka*o[12]+Ts*o[10]+nl*o[4]*Bo[0]+vn*o[6]*Co[0]+la*o[3]*vo[0]+Gl*o[5]*Wo[0]+Cr*o[7]*Yo[0]+mo*o[8]*Oo[0]+ud*o[11]+ui*o[9],sc=zs*n[0]*Xo[0]+Fc*n[1]*tn[0]+Sa*n[2]*Mo[0]+Ya*n[12]+ms*n[10]+ol*n[4]*Bo[0]+wn*n[6]*Co[0]+ia*n[3]*vo[0]+Bl*n[5]*Wo[0]+Ur*n[7]*Yo[0]+go*n[8]*Oo[0]+dd*n[11]+di*n[9],ic=Ls*i[0]*Xo[0]+Wc*i[1]*tn[0]+Ra*i[2]*Mo[0]+Ha*i[12]+gs*i[10]+al*i[4]*Bo[0]+hn*i[6]*Co[0]+F*i[3]*vo[0]+Dl*i[5]*Wo[0]+zr*i[7]*Yo[0]+po*i[8]*Oo[0]+cd*i[11]+ci*i[9],Qr=vr*l[7]*hi[1]+to*l[8]*Ai[1]+Jc*l[11]+$n*l[9],'cav'==ji?($s=Hi*l[4]*Ei[1]+un*l[6]*fi[1]+Qr,ac=ws*l[0]*wi[1]+zc*l[1]*vi[1]+ma*l[2]*_i[1]+xa*l[12]*na+os*l[10],Zs=U*l[3]*Ti[1]+kl*l[5]*bi[1]):'inf'==ji?(ac=ws*l[0]*wi[1]+zc*l[1]*vi[1]+ma*l[2]*_i[1]+xa*l[12]*na+os*l[10]+Qr,$s=Hi*l[4]*Ei[1]+un*l[6]*fi[1],Zs=U*l[3]*Ti[1]+kl*l[5]*bi[1]):'arc'==ji?(ac=ws*l[0]*wi[1]+zc*l[1]*vi[1]+ma*l[2]*_i[1]+xa*l[12]*na+os*l[10],$s=Hi*l[4]*Ei[1]+un*l[6]*fi[1],
  Zs=U*l[3]*Ti[1]+kl*l[5]*bi[1]+Qr):(ac=ws*l[0]*wi[1]+zc*l[1]*vi[1]+ma*l[2]*_i[1]+xa*l[12]*na+os*l[10],$s=Hi*l[4]*Ei[1]+un*l[6]*fi[1],Zs=U*l[3]*Ti[1]+kl*l[5]*bi[1]),Ys=(qs=ac*Zt)+(js=$s*Zt)+(Bs=Zs*Zt),wc=(bc=uc*Vt)+(Ac=sc*Vt)+(mc=ic*Vt),console.log(Ys,wc),Vi=0==qs||0==bc?0:bc<=qs?1:Math.pow(qs/bc,.5)/(bc/qs),Di=0==js||0==Ac?0:Ac<=js?1:Math.pow(js/Ac,.5)/(Ac/js),Ci=0==Bs||0==mc?0:mc<=Bs?1:Math.pow(Bs/mc,.5)/(mc/Bs),Li=0==qs||0==bc?0:qs<=bc?1:Math.pow(bc/qs,.5)/(qs/bc),Ii=0==js||0==Ac?0:js<=Ac?1:Math.pow(Ac/js,.5)/(js/Ac),yi=0==Bs||0==mc?0:Bs<=mc?1:Math.pow(mc/Bs,.5)/(Bs/mc),Gn=Math.round(1e6*Li)/1e6,Bn=Math.round(1e6*Ii)/1e6,Dn=Math.round(1e6*yi)/1e6,Wn=Math.round(1e6*Vi)/1e6,jn=Math.round(1e6*Di)/1e6,Vn=Math.round(1e6*Ci)/1e6,Ss=Math.round(-Us*Wn+1e-6)+Math.round(-zs*jn+1e-6)+Math.round(-Ls*Vn+1e-6),xc=Math.round(-qc*Wn+1e-6)+Math.round(-Fc*jn+1e-6)+Math.round(-Wc*Vn+1e-6),Aa=Math.round(-ya*Wn+1e-6)+Math.round(-Sa*jn+1e-6)+Math.round(-Ra*Vn+1e-6),
  P=Math.round(-la*Wn+1e-6)+Math.round(-ia*jn+1e-6)+Math.round(-F*Vn+1e-6),Qi=Math.round(-nl*Wn+1e-6)+Math.round(-ol*jn+1e-6)+Math.round(-al*Vn+1e-6),Ol=Math.round(-Gl*Wn+1e-6)+Math.round(-Bl*jn+1e-6)+Math.round(-Dl*Vn+1e-6),mn=Math.round(-vn*Wn+1e-6)+Math.round(-wn*jn+1e-6)+Math.round(-hn*Vn+1e-6),yr=Math.round(-Cr*Wn+1e-6)+Math.round(-Ur*jn+1e-6)+Math.round(-zr*Vn+1e-6),no=Math.round(-mo*Wn+1e-6)+Math.round(-go*jn+1e-6)+Math.round(-po*Vn+1e-6),ad=Math.round(-ud*Wn+1e-6)+Math.round(-dd*jn+1e-6)+Math.round(-cd*Vn+1e-6),Ba=Math.round(-Ka*Wn+1e-6)+Math.round(-Ya*jn+1e-6)+Math.round(-Ha*Vn+1e-6),ai=Math.round(-ui*Wn+1e-6)+Math.round(-di*jn+1e-6)+Math.round(-ci*Vn+1e-6),ls=Math.round(-Ts*Wn+1e-6)+Math.round(-ms*jn+1e-6)+Math.round(-gs*Vn+1e-6),bs=Math.round(-ws*Gn+1e-6),Oc=Math.round(-zc*Gn+1e-6),pa=Math.round(-ma*Gn+1e-6),Ua=Math.round(-xa*Gn+1e-6),L=Math.round(-U*Dn+1e-6),yl=Math.round(-kl*Dn+1e-6),Fi=Math.round(-Hi*Bn+1e-6),cn=Math.round(-un*Bn+1e-6),'cav'==ji?(hr=Math.round(-vr*Bn+1e-6),
  $a=Math.round(-to*Bn+1e-6),Zc=Math.round(-Jc*Bn+1e-6),Xn=Math.round(-$n*Bn+1e-6)):'inf'==ji?(hr=Math.round(-vr*Gn+1e-6),$a=Math.round(-to*Gn+1e-6),Zc=Math.round(-Jc*Gn+1e-6),Xn=Math.round(-$n*Gn+1e-6)):'arc'==ji&&(hr=Math.round(-vr*Dn+1e-6),$a=Math.round(-to*Dn+1e-6),Zc=Math.round(-Jc*Dn+1e-6),Xn=Math.round(-$n*Dn+1e-6)),ts=100*os==ar?0:Math.round(-os*Gn+1e-6),vs=ws+bs,Uc=zc+Oc,Ta=ma+pa,C=U+L,Yi=Hi+Fi,Il=kl+yl,pn=un+cn,Rr=vr+hr,ao=to+$a,$c=Jc+Zc,Pa=xa+Ua,ns=os+ts,ei=$n+Xn,Os=Ms+Ss,jc=Dc+xc,va=Ea+Aa,W=B+P,tl=Xi+Qi,Pl=zl+Ol,bn=_n+mn,Lr=kr+yr,uo=lo+no,sd=nd+ad,qa=Va+Ba,ps=ss+ls,si=ni+ai,dl=bs*r[0]+Oc*r[1]+pa*r[2]+L*r[3]+Fi*r[4]+yl*r[5]+cn*r[6]+hr*r[7]+$a*r[8]+Xn*r[9]+ts*r[10]+Zc*r[11]+Ua*r[12],gl=Ss*r[0]+xc*r[1]+Aa*r[2]+P*r[3]+Qi*r[4]+Ol*r[5]+mn*r[6]+yr*r[7]+no*r[8]+ai*r[9]+ls*r[10]+ad*r[11]+Ba*r[12],console.log(dl,gl),Sc=vs*r[0]+Uc*r[1]+Ta*r[2]+C*r[3]+Yi*r[4]+Il*r[5]+pn*r[6]+Rr*r[7]+ao*r[8]+$c*r[11]+Pa*r[12]+ns*r[10]+ei*r[9],
  kc=Os*r[0]+jc*r[1]+va*r[2]+W*r[3]+tl*r[4]+Pl*r[5]+bn*r[6]+Lr*r[7]+uo*r[8]+sd*r[11]+qa*r[12]+ps*r[10]+si*r[9],console.log(kc,Sc),lr=vs*r[0]+Uc*r[1]+Ta*r[2]+C*r[3]+Yi*r[4]+Il*r[5]+pn*r[6]+Rr*r[7]+ao*r[8]+ei*r[9]+ns*r[10]+$c*r[11]+Pa*r[12],fr=Os*r[0]+jc*r[1]+va*r[2]+W*r[3]+tl*r[4]+Pl*r[5]+bn*r[6]+Lr*r[7]+uo*r[8]+si*r[9]+ps*r[10]+sd*r[11]+qa*r[12],console.log(lr,fr),Fr=Rr*r[7]+ao*r[8]+$c*r[11]+ei*r[9],'cav'==ji?(er=Yi*r[4]+pn*r[6]+Fr,or=vs*r[0]+Uc*r[1]+Ta*r[2]+Pa*r[12]+ns*r[10],Xl=C*r[3]+Il*r[5]):'inf'==ji?(or=vs*r[0]+Uc*r[1]+Ta*r[2]+Pa*r[12]+ns*r[10]+Fr,er=Yi*r[4]+pn*r[6],Xl=C*r[3]+Il*r[5]):'arc'==ji?(or=vs*r[0]+Uc*r[1]+Ta*r[2]+Pa*r[12]+ns*r[10],er=Yi*r[4]+pn*r[6],Xl=C*r[3]+Il*r[5]+Fr):(or=vs*r[0]+Uc*r[1]+Ta*r[2]+Pa*r[12]+ns*r[10],er=Yi*r[4]+pn*r[6],Xl=C*r[3]+Il*r[5]),vl=0==or?0:Math.round(or/lr*1e4)/1e4,wl=0==er?0:Math.round(er/lr*1e4)/1e4,hl=0==Xl?0:Math.round(Xl/lr*1e4)/1e4,console.log(hl),Ps=Math.round(Os*vl),xs=Math.round(Os*wl),Cs=Os-xs-Ps,Kc=Math.round(jc*vl),Yc=Math.round(jc*wl),
  Hc=jc-Yc-Kc,Ia=Math.round(va*vl),ka=Math.round(va*wl),Ma=va-ka-Ia,ca=Math.round(W*vl),sa=Math.round(W*wl),ra=W-sa-ca,rl=Math.round(tl*vl),ll=Math.round(tl*wl),il=tl-ll-rl,Wl=Math.round(Pl*vl),jl=Math.round(Pl*wl),Vl=Pl-jl-Wl,yn=Math.round(bn*vl),Sn=Math.round(bn*wl),Rn=bn-Sn-yn,Dr=Math.round(Lr*vl),Pr=Math.round(Lr*wl),xr=Lr-Pr-Dr,Ao=Math.round(uo*vl),_o=Math.round(uo*wl),To=uo-_o-Ao,fs=Math.round(ps*vl),As=Math.round(ps*wl),_s=ps-As-fs,md=Math.round(sd*vl),gd=Math.round(sd*wl),pd=sd-gd-md,Xa=Math.round(qa*vl),Za=Math.round(qa*wl),Qa=qa-Za-Xa,mi=Math.round(si*vl),gi=Math.round(si*wl),pi=si-gi-mi,Tr=Ps*r[0]+Kc*r[1]+Ia*r[2]+Xa*r[12]+fs*r[10]+rl*r[4]+yn*r[6]+ca*r[3]+Wl*r[5]+Dr*r[7]+Ao*r[8]+md*r[11]+mi*r[9],pr=xs*r[0]+Yc*r[1]+ka*r[2]+Za*r[12]+As*r[10]+ll*r[4]+Sn*r[6]+sa*r[3]+jl*r[5]+Pr*r[7]+_o*r[8]+gd*r[11]+gi*r[9],cr=Cs*r[0]+Hc*r[1]+Ma*r[2]+Qa*r[12]+_s*r[10]+il*r[4]+Rn*r[6]+ra*r[3]+Vl*r[5]+xr*r[7]+To*r[8]+pd*r[11]+pi*r[9],console.log(cr,pr,Tr),
  pc=Ps*o[0]*Xo[0]+Kc*o[1]*tn[0]+Ia*o[2]*Mo[0]+Xa*o[12]+fs*o[10]+rl*o[4]*Bo[0]+yn*o[6]*Co[0]+ca*o[3]*vo[0]+Wl*o[5]*Wo[0]+Dr*o[7]*Yo[0]+Ao*o[8]*Oo[0]+md*o[11]+mi*o[9],cc=xs*n[0]*Xo[0]+Yc*n[1]*tn[0]+ka*n[2]*Mo[0]+Za*n[12]+As*n[10]+ll*n[4]*Bo[0]+Sn*n[6]*Co[0]+sa*n[3]*vo[0]+jl*n[5]*Wo[0]+Pr*n[7]*Yo[0]+_o*n[8]*Oo[0]+gd*n[11]+gi*n[9],lc=Cs*i[0]*Xo[0]+Hc*i[1]*tn[0]+Ma*i[2]*Mo[0]+Qa*i[12]+_s*i[10]+il*i[4]*Bo[0]+Rn*i[6]*Co[0]+ra*i[3]*vo[0]+Vl*i[5]*Wo[0]+xr*i[7]*Yo[0]+To*i[8]*Oo[0]+pd*i[11]+pi*i[9],Yr=Rr*l[7]*hi[1]+ao*l[8]*Ai[1]+$c*l[11]+ei*l[9],'cav'==ji?(ec=Yi*l[4]*Ei[1]+pn*l[6]*fi[1]+Yr,oc=vs*l[0]*wi[1]+Uc*l[1]*vi[1]+Ta*l[2]*_i[1]+Pa*l[12]*na+ns*l[10],Xs=C*l[3]*Ti[1]+Il*l[5]*bi[1]):'inf'==ji?(oc=vs*l[0]*wi[1]+Uc*l[1]*vi[1]+Ta*l[2]*_i[1]+Pa*l[12]*na+ns*l[10]+Yr,ec=Yi*l[4]*Ei[1]+pn*l[6]*fi[1],Xs=C*l[3]*Ti[1]+Il*l[5]*bi[1]):'arc'==ji?(oc=vs*l[0]*wi[1]+Uc*l[1]*vi[1]+Ta*l[2]*_i[1]+Pa*l[12]*na+ns*l[10],ec=Yi*l[4]*Ei[1]+pn*l[6]*fi[1],
  Xs=C*l[3]*Ti[1]+Il*l[5]*bi[1]+Yr):(oc=vs*l[0]*wi[1]+Uc*l[1]*vi[1]+Ta*l[2]*_i[1]+Pa*l[12]*na+ns*l[10],ec=Yi*l[4]*Ei[1]+pn*l[6]*fi[1],Xs=C*l[3]*Ti[1]+Il*l[5]*bi[1]),Ks=(Hs=oc*Zt)+(Ws=ec*Zt)+(Gs=Xs*Zt),vc=(hc=pc*Vt)+(fc=cc*Vt)+(Tc=lc*Vt),console.log(Ks,vc),Bi=0==Hs||0==hc?0:hc<=Hs?1:Math.pow(Hs/hc,.5)/(hc/Hs),xi=0==Ws||0==fc?0:fc<=Ws?1:Math.pow(Ws/fc,.5)/(fc/Ws),zi=0==Gs||0==Tc?0:Tc<=Gs?1:Math.pow(Gs/Tc,.5)/(Tc/Gs),Ni=0==Hs||0==hc?0:Hs<=hc?1:Math.pow(hc/Hs,.5)/(Hs/hc),Mi=0==Ws||0==fc?0:Ws<=fc?1:Math.pow(fc/Ws,.5)/(Ws/fc),Ri=0==Gs||0==Tc?0:Gs<=Tc?1:Math.pow(Tc/Gs,.5)/(Gs/Tc),Hn=Math.round(1e6*Ni)/1e6,qn=Math.round(1e6*Mi)/1e6,Fn=Math.round(1e6*Ri)/1e6,Qn=Math.round(1e6*Bi)/1e6,Kn=Math.round(1e6*xi)/1e6,Yn=Math.round(1e6*zi)/1e6,ys=Math.round(-Ps*Qn+1e-6)+Math.round(-xs*Kn+1e-6)+Math.round(-Cs*Yn+1e-6),Pc=Math.round(-Kc*Qn+1e-6)+Math.round(-Yc*Kn+1e-6)+Math.round(-Hc*Yn+1e-6),fa=Math.round(-Ia*Qn+1e-6)+Math.round(-ka*Kn+1e-6)+Math.round(-Ma*Yn+1e-6),
  D=Math.round(-ca*Qn+1e-6)+Math.round(-sa*Kn+1e-6)+Math.round(-ra*Yn+1e-6),Zi=Math.round(-rl*Qn+1e-6)+Math.round(-ll*Kn+1e-6)+Math.round(-il*Yn+1e-6),Ll=Math.round(-Wl*Qn+1e-6)+Math.round(-jl*Kn+1e-6)+Math.round(-Vl*Yn+1e-6),Tn=Math.round(-yn*Qn+1e-6)+Math.round(-Sn*Kn+1e-6)+Math.round(-Rn*Yn+1e-6),Mr=Math.round(-Dr*Qn+1e-6)+Math.round(-Pr*Kn+1e-6)+Math.round(-xr*Yn+1e-6),io=Math.round(-Ao*Qn+1e-6)+Math.round(-_o*Kn+1e-6)+Math.round(-To*Yn+1e-6),od=Math.round(-md*Qn+1e-6)+Math.round(-gd*Kn+1e-6)+Math.round(-pd*Yn+1e-6),Ga=Math.round(-Xa*Qn+1e-6)+Math.round(-Za*Kn+1e-6)+Math.round(-Qa*Yn+1e-6),oi=Math.round(-mi*Qn+1e-6)+Math.round(-gi*Kn+1e-6)+Math.round(-pi*Yn+1e-6),rs=Math.round(-fs*Qn+1e-6)+Math.round(-As*Kn+1e-6)+Math.round(-_s*Yn+1e-6),hs=Math.round(-vs*Hn+1e-6),Lc=Math.round(-Uc*Hn+1e-6),ga=Math.round(-Ta*Hn+1e-6),Ca=Math.round(-Pa*Hn+1e-6),z=Math.round(-C*Fn+1e-6),Ml=Math.round(-Il*Fn+1e-6),qi=Math.round(-Yi*qn+1e-6),dn=Math.round(-pn*qn+1e-6),
  'cav'==ji?(wr=Math.round(-Rr*qn+1e-6),eo=Math.round(-ao*qn+1e-6),Xc=Math.round(-$c*qn+1e-6),Jn=Math.round(-ei*qn+1e-6)):'inf'==ji?(wr=Math.round(-Rr*Hn+1e-6),eo=Math.round(-ao*Hn+1e-6),Xc=Math.round(-$c*Hn+1e-6),Jn=Math.round(-ei*Hn+1e-6)):'arc'==ji&&(wr=Math.round(-Rr*Fn+1e-6),eo=Math.round(-ao*Fn+1e-6),Xc=Math.round(-$c*Fn+1e-6),Jn=Math.round(-ei*Fn+1e-6)),as=100*ns==or?0:Math.round(-ns*Hn+1e-6),wt=vs+hs,vt=Uc+Lc,Rt=Ta+ga,St=C+z,Ot=Yi+qi,Lt=Il+Ml,zt=pn+dn,kt=Rr+wr,It=ao+eo,Nt=$c+Xc,Ut=Pa+Ca,yt=ns+as,Mt=ei+Jn,Re=Os+ys,Se=jc+Pc,ye=va+fa,Me=W+D,ze=tl+Zi,Ue=Pl+Ll,Ce=bn+Tn,Ne=Lr+Mr,Oe=uo+io,Le=sd+od,xe=qa+Ga,ke=ps+rs,Ie=si+oi,ul=hs*r[0]+Lc*r[1]+ga*r[2]+z*r[3]+qi*r[4]+Ml*r[5]+dn*r[6]+wr*r[7]+eo*r[8]+Jn*r[9]+as*r[10]+Xc*r[11]+Ca*r[12],ml=ys*r[0]+Pc*r[1]+fa*r[2]+D*r[3]+Zi*r[4]+Ll*r[5]+Tn*r[6]+Mr*r[7]+io*r[8]+oi*r[9]+rs*r[10]+od*r[11]+Ga*r[12],console.log(ul,ml),yc=wt*r[0]+vt*r[1]+Rt*r[2]+St*r[3]+Ot*r[4]+Lt*r[5]+zt*r[6]+kt*r[7]+It*r[8]+Nt*r[11]+Ut*r[12]+yt*r[10]+Mt*r[9],
  Ic=Re*r[0]+Se*r[1]+ye*r[2]+Me*r[3]+ze*r[4]+Ue*r[5]+Ce*r[6]+Ne*r[7]+Oe*r[8]+Le*r[11]+xe*r[12]+ke*r[10]+Ie*r[9],console.log(Ic,yc),Ze=-Pe+wt,Xe=-De+vt,Je=-Be+Rt,$e=-Ge+St,it=-He+Ot,lt=-Ye+Lt,rt=-Ke+zt,at=-We+kt,ot=-Fe+It,nt=-qe+Nt,st=-Qe+Ut,et=-Ve+yt,tt=-je+Mt,ne=-q+Re,ie=-H+Se,le=-Y+ye,re=-K+Me,ge=-ee+ze,me=-te+Ue,Te=-ae+Ce,de=-X+Ne,ue=-J+Oe,pe=-$+Le,_e=-oe+xe,se=-Q+ke,ce=-Z+Ie,jr=0==kt?0:kt*Zt*On[1],In=20==Dt?18:19==Dt?17:18==Dt?15:17==Dt?14:16==Dt?13:15==Dt?11:14==Dt?10:13==Dt||12==Dt?9:11==Dt?8:10==Dt?7:9==Dt||8==Dt?6:7==Dt||6==Dt?5:5==Dt||4==Dt||3==Dt?4:2==Dt||1==Dt?3:0,Br=0==Oa?0:2*In,Gr=0==jr||0==Br?0:-jr/Br,rn=Dt<=$r?Dt:Dt-$r<-Gr?Dt<$r?Dt:$r:Dt+Gr,Bt=Math.round(rn),xt='wall'==E?Bt:v[t.BATTLE_TARGET_LEVEL],bo=0==It||0==xt?0:It*Zt*Ai[1],
  Mn='headquarter'==E?30==xt?72:29==xt?65:28==xt?59:27==xt?54:26==xt?50:25==xt?45:24==xt?41:23==xt?36:22==xt?33:21==xt?30:20==xt?27:19==xt?26:18==xt?23:17==xt?14:16==xt?13:15==xt?11:14==xt?10:13==xt||12==xt?9:11==xt?8:10==xt?7:9==xt||8==xt?6:7==xt||6==xt?5:5==xt||4==xt||3==xt?4:2==xt||1==xt?3:0:'wall'==E?20==Bt?18:19==Bt?17:18==Bt?15:17==Bt?14:16==Bt?13:15==Bt?11:14==Bt?10:13==Bt||12==Bt?9:11==Bt?8:10==Bt?7:9==Bt||8==Bt?6:7==Bt||6==Bt?5:5==Bt||4==Bt||3==Bt?4:2==Bt||1==Bt?3:0:'tavern'==E?15==xt?11:14==xt?10:13==xt||12==xt?9:11==xt?8:10==xt?7:9==xt||8==xt?6:7==xt||6==xt?5:5==xt||4==xt||3==xt?4:2==xt||1==xt?3:0:'hospital'==E?10==xt?7:9==xt||8==xt?6:7==xt||6==xt?5:5==xt||4==xt||3==xt?4:2==xt||1==xt?3:0:'satue'==E?5==xt||4==xt||3==xt?4:2==xt||1==xt?3:0:'rally_point'==E?5==xt||4==xt||3==xt?4:2==xt||1==xt?3:0:'church'==E?3==xt||2==xt||1==xt?800:0:'academy'==E?1==xt?500:0:'preceptory'==E?10==xt?71:9==xt?43:8==xt?39:7==xt?35:6==xt?32:5==xt?29:4==xt?27:3==xt?24:2==xt?22:1==xt?20:0:'barracks'==E?25==xt?30:24==xt?27:23==xt?24:22==xt?22:21==xt?20:20==xt?18:19==xt?17:18==xt?15:17==xt?14:16==xt?13:15==xt?11:14==xt?10:13==xt||12==xt?9:11==xt?8:10==xt?7:9==xt||8==xt?6:7==xt||6==xt?5:5==xt||4==xt||3==xt?4:2==xt||1==xt?3:0:'market'==E?25==xt?30:24==xt?27:23==xt?24:22==xt?22:21==xt?20:20==xt?18:19==xt?17:18==xt?15:17==xt?14:16==xt?13:15==xt?11:14==xt?10:13==xt||12==xt?9:11==xt?8:10==xt?7:9==xt||8==xt?6:7==xt||6==xt?5:5==xt||4==xt||3==xt?4:2==xt||1==xt?3:0:'warehouse'==E?30==xt?48:29==xt?43:28==xt?39:27==xt?36:26==xt?33:25==xt?30:24==xt?27:23==xt?24:22==xt?22:21==xt?20:20==xt?18:19==xt?17:18==xt?15:17==xt?14:16==xt?13:15==xt?11:14==xt?10:13==xt||12==xt?9:11==xt?8:10==xt?7:9==xt||8==xt?6:7==xt||6==xt?5:5==xt||4==xt||3==xt?4:2==xt||1==xt?3:0:'timber_camp'==E?30==xt?48:29==xt?43:28==xt?39:27==xt?36:26==xt?33:25==xt?30:24==xt?27:23==xt?24:22==xt?22:21==xt?20:20==xt?18:19==xt?17:18==xt?15:17==xt?14:16==xt?13:15==xt?11:14==xt?10:13==xt||12==xt?9:11==xt?8:10==xt?7:9==xt||8==xt?6:7==xt||6==xt?5:5==xt||4==xt||3==xt?4:2==xt||1==xt?3:0:'clay_pit'==E?30==xt?48:29==xt?43:28==xt?39:27==xt?36:26==xt?33:25==xt?30:24==xt?27:23==xt?24:22==xt?22:21==xt?20:20==xt?18:19==xt?17:18==xt?15:17==xt?14:16==xt?13:15==xt?11:14==xt?10:13==xt||12==xt?9
  